Методы getter и setter - это методы доступа, что означает, что они обычно представляют собой открытый интерфейс для изменения членов частного класса. Вы используете методы getter и setter для определения свойства. Вы используете методы getter и setter как свойства вне класса, даже если вы определяете их в классе как методы. Эти свойства вне класса могут иметь другое имя от имени свойства в классе.
Есть некоторые преимущества использования методов getter и setter, таких как возможность позволить вам создавать элементы со сложными функциями, которые вы может получить доступ к таким свойствам. Они также позволяют создавать свойства только для чтения и только для записи.
Несмотря на то, что методы getter и setter полезны, вы должны быть осторожны, чтобы не злоупотреблять ими, потому что, помимо прочих проблем, они могут улучшить обслуживание кода трудно в определенных ситуациях. Кроме того, они предоставляют доступ к вашей реализации класса, например, к публичным пользователям. Практика ООП препятствует прямому доступу к свойствам внутри класса.
Когда вы пишете классы, вам всегда рекомендуется делать как можно больше ваших переменных экземпляра частным и соответственно добавлять методы getter и setter. Это происходит потому, что есть несколько раз, когда вы не можете позволить пользователям изменять определенные переменные в ваших классах. Например, если у вас есть частный статический метод, который отслеживает количество экземпляров, созданных для определенного класса, вы не хотите, чтобы пользователь модифицировал этот счетчик с помощью кода. Только инструкция конструктора должна увеличивать эту переменную всякий раз, когда она вызывается. В этой ситуации вы можете создать переменную частного экземпляра и разрешить метод getter только для переменной счетчика, что означает, что пользователи могут извлекать текущее значение только с помощью метода getter, и они не смогут устанавливать новые значения используя метод setter. Создание геттера без сеттера - это простой способ сделать определенные переменные в вашем классе доступными только для чтения.
Чтобы получить копию, используйте метод 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 ...
Используйте ActiveRecord :: Base # dup , если вы не хотите копировать id
Вы также можете проверить 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">
Надеюсь, это может вам помочь.
Я обычно просто копирую атрибуты, меняя все, что мне нужно, меняя:
new_user = User.new(old_user.attributes.merge(:login => "newlogin"))
Поскольку при дублировании модели может быть больше логики, я бы предложил создать новый класс, где вы будете обрабатывать всю необходимую логику. Чтобы облегчить это, есть камень, который может помочь: 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'}
В зависимости от ваших потребностей и стиля программирования вы также можете использовать комбинацию нового метода класса и слияния. Из-за отсутствия лучшего примера предположим, что у вас есть задание, запланированное на определенную дату, и вы хотите дублировать его на другую дату. Фактические атрибуты задачи не важны, поэтому:
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.
Мир.
Если вам нужна глубокая копия с ассоциациями, я рекомендую deep_cloneable gem.
Легкий способ:
#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
Вам также может понравиться 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 есть еще больше возможностей, поэтому обязательно проверьте документацию.
Наслаждайтесь! :)
Вот пример переопределения метода 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