acts_as_brand_new_copy
Copy an active record with its associated records are not easy.
For example, if we have defined following classes:
class Grade < ActiveRecord::Base
has_and_belongs_to_many :teachers, :join_table => ::GradeTeacherAssignment.table_name
has_and_belongs_to_many :students, :join_table => ::GradeStudentAssignment.table_name
end
class Teacher < ActiveRecord::Base
has_many :student_teacher_assignments
has_many :students,
:through => :student_teacher_assignments,
:source => :student
end
class Student < ActiveRecord::Base
has_many :student_teacher_assignments
has_many :teachers,
:through => :student_teacher_assignments,
:source => :teacher
has_many :scores
end
class Score < ActiveRecord::Base
belongs_to :student
end
Can you copy a grade with its teachers and students to another grade in a few lines of code, keeping the relationships between teachers and students? To me, it's no, consequently acts_as_brand_new_copy was born.
Usage
copy an active record with its associations
# copy student itself, return the id for copied student
copy_id = @student.brand_new_copy
# copy student with their scores
copy_id = @student.brand_new_copy({:associations => [:scores]})
# copy the whole grade and all the relationships between grade to students, teachers to students
# NOTE here shows the convenience bought by this gem, we've ensured that a same student won't be copied twice!
copy_id = @grade.brand_new_copy({:associations => [{:teachers => [:students]}, :students]})
i'd like to do some modifications to records during copy process
Don't worry, we've already supported that!
# prefix student name with a 'Copy Of ' during copy
# a callback defined as a class method is needed
Student.class_eval do
def self.update_name_when_copy(hash_origin, hash_copy, full_context)
hash_copy['name'] = 'Copy of ' + hash_origin['name']
true
end
end
copy_id = @student.brand_new_copy({:callbacks => [:update_name_when_copy]})
# prefix grade, students, teachers name with 'Copy of ', and reset students score to nil during copy
[Grade, Teacher, Student].each do |klass|
klass.class_eval do
def self.update_name_when_copy(hash_origin, hash_copy, full_context)
hash_copy['name'] = 'Copy Of ' + hash_origin['name']
true
end
end
end
Score.class_eval do
def self.reset_value_when_copy(hash_origin, hash_copy, full_context)
hash_copy['value'] = nil
true
end
end
copy_id = @grade.brand_new_copy({
:associations => [{:teachers => [:students]}, {:students => [:scores]}],
:callbacks => [
:update_name_when_copy,
{:teachers => [:update_name_when_copy]},
{:students => [:update_name_when_copy, {:scores => [:reset_value_when_copy]}]}
]
})
Installation
Add this line to your application's Gemfile:
gem 'acts_as_brand_new_copy'
And then execute:
$ bundle
Or install it yourself as:
$ gem install acts_as_brand_new_copy
Current Limitation
- do not support has_many_and_belongs_to_many associations when join table class has a strange table_name(I mean, table_name not in [Class.name.underscore, Class.name.underscore.pluralize])
Contribute
You're highly welcome to improve this gem.
Checkout source code to local
say you git clone the source code to /tmp/acts_as_brand_new_copy
Install dev bundle
$ cd /tmp/acts_as_brand_new_copy
$ bundle install
Do some changes
$ vi lib/acts_as_brand_new_copy.rb
Run test
$ bundle exec rspec spec