sequel_deep_dup 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: dde7ced04065758f9cc497072032be96caf0795d
4
+ data.tar.gz: 5aa9acc6054864fdd7fa8360217edcafa36fb2eb
5
+ SHA512:
6
+ metadata.gz: e7531307317303235bcd0596701770aad8b126b04c9cc1da3d42663dcb15dc59acccb357a6cd9caf92b95971e8d66cb5547b68a06abf538bc70910a610216b75
7
+ data.tar.gz: a7868f64e6cec794d6d4867d3794d954920e16787da4a60b74932b31433620914b8459e7c543c3c0e9273cf3d8a096a22949865e489f20af29cab0587eaf81b6
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.swp
19
+ *.swo
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sequel_deep_dup.gemspec
4
+ gemspec
5
+
6
+ gem 'rspec'
7
+ gem 'sqlite3'
8
+ gem 'sequel'
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 macario
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # Sequel::DeepDup
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'sequel_deep_dup'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install sequel_deep_dup
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( http://github.com/maca/sequel_deep_dup/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,72 @@
1
+ require 'sequel'
2
+ require 'sequel/plugins/instance_hooks'
3
+
4
+ module Sequel
5
+ module Plugins
6
+ module DeepDup
7
+ class DeepDupper
8
+ attr_reader :instance, :omit_records
9
+
10
+ def initialize instance, omit = []
11
+ @instance = instance
12
+ @omit_records = omit
13
+ end
14
+
15
+ def dup_associations instance, copy, associations
16
+ associations.each do |name|
17
+ next unless refl = instance.class.association_reflection(name)
18
+ [*instance.send(name)].compact.each { |rec| instantiate_associated(copy, refl, rec) }
19
+ end
20
+ end
21
+
22
+ def dup
23
+ copy = shallow_dup.extend(InstanceHooks::InstanceMethods)
24
+ omit_records << instance
25
+ dup_associations(instance, copy, instance.class.associations)
26
+ copy
27
+ end
28
+
29
+ def shallow_dup
30
+ klass = instance.class
31
+ attributes = instance.to_hash.dup
32
+ [*klass.primary_key].each { |attr| attributes.delete(attr) }
33
+ klass.new attributes
34
+ end
35
+
36
+ private
37
+ def instantiate_associated copy, reflection, record
38
+ return if omit_records.detect { |to_omit| record.pk == to_omit.pk && record.class == to_omit.class }
39
+
40
+ unless reflection[:type] == :many_to_many
41
+ record = DeepDupper.new(record, omit_records).dup
42
+ end
43
+
44
+ if reflection.returns_array?
45
+ copy.send(reflection[:name]) << record
46
+ copy.after_save_hook{ copy.send(reflection.add_method, record) }
47
+ else
48
+ copy.associations[reflection[:name]] = record
49
+
50
+ copy.instance_variable_set :@set_associated_object_if_same, true
51
+
52
+ if reflection[:type] == :many_to_one
53
+ copy.before_save_hook {
54
+ copy.send reflection.setter_method, record.save(:validate=>false)
55
+ }
56
+ else
57
+ copy.after_save_hook{
58
+ copy.send(reflection.setter_method, record)
59
+ }
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ module InstanceMethods
66
+ def deep_dup
67
+ DeepDupper.new(self).dup
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,7 @@
1
+ module Sequel
2
+ module Plugins
3
+ module DeepDup
4
+ VERSION = "0.1.1"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sequel_deep_dup/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sequel_deep_dup"
8
+ spec.version = Sequel::Plugins::DeepDup::VERSION
9
+ spec.authors = ["macario"]
10
+ spec.email = ["mail@makarius.me"]
11
+ spec.summary = %q{Makes deep copies of existing sequel models into a new record, along with its association tree}
12
+ spec.description = %q{Makes deep copies of existing sequel models into a new record, along with its association tree}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
+ spec.add_development_dependency "rake"
23
+ end
@@ -0,0 +1,127 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sequel::Plugins::DeepDup do
4
+ let(:program) { Program.create name: 'CS' }
5
+ let(:program_copy) { program.deep_dup }
6
+ let(:course) { Course.create name: 'Ruby', program: program }
7
+ let(:course_copy) { course.deep_dup }
8
+ let(:student) { Student.create name: 'Macario' }
9
+ let(:student_copy) { student.deep_dup }
10
+ let(:enrollment) { Enrollment.create(student_id: student.id, course_id: course.id) }
11
+ let(:enrollment_copy) { enrollment.deep_dup }
12
+
13
+ describe 'duplicating plain record' do
14
+
15
+ before do
16
+ Program.plugin :deep_dup
17
+ Enrollment.plugin :deep_dup
18
+ end
19
+
20
+ it { program_copy.name.should == 'CS' }
21
+ it { program_copy.pk.should be_nil }
22
+ it { program_copy.should be_new }
23
+ it { enrollment_copy.pk.should == [nil, nil] }
24
+ it { enrollment_copy.should be_new }
25
+ end
26
+
27
+ describe 'duplicating record with a one to many association' do
28
+ before do
29
+ Program.plugin :deep_dup
30
+ 3.times { |num| program.add_course name: "Course #{num}" }
31
+ end
32
+
33
+ it { program_copy.should have(3).courses }
34
+ it { program_copy.courses.map(&:pk).should be_none }
35
+ it { expect { program_copy.save }.to change{ Course.count }.to(6) }
36
+ end
37
+
38
+ describe 'duplicating record with a many to many association' do
39
+ before do
40
+ Course.plugin :deep_dup
41
+ 3.times { |num| course.add_category name: "Category #{num}" }
42
+ end
43
+
44
+ it { course_copy.should have(3).categories }
45
+ it { course_copy.categories.should == course.categories }
46
+ it { expect { course_copy.save }.not_to change{ Category.count } }
47
+ end
48
+
49
+ describe 'duplicating record with nested many to one' do
50
+ before do
51
+ Program.plugin :deep_dup
52
+ 3.times { |num| program.add_course name: "Course #{num}" }
53
+
54
+ program.courses.each do |course|
55
+ 3.times { |num| course.add_assignment name: "Assignment #{num}" }
56
+ end
57
+ end
58
+
59
+ it { program_copy.should have(3).courses }
60
+ it { program_copy.courses.first.should have(3).assignments }
61
+ it { expect { program_copy.save }.to change{ Course.count }.to(6) }
62
+ it { expect { program_copy.save }.to change{ Assignment.count }.to(18) }
63
+ end
64
+
65
+ describe 'duplicating record with a many to many association after a many to one' do
66
+ before do
67
+ Program.plugin :deep_dup
68
+ 3.times { |num| program.add_course name: "Course #{num}" }
69
+ program.courses.each do |course|
70
+ 3.times { |num| course.add_category name: "Category #{num}" }
71
+ end
72
+ end
73
+
74
+ it { program_copy.should have(3).courses }
75
+ it { program_copy.courses.first.should have(3).categories }
76
+ it { program_copy.courses.last.should have(3).categories }
77
+ it { expect { program_copy.save }.not_to change{ Category.count } }
78
+ end
79
+
80
+ describe 'duplicating record with one to one' do
81
+ before do
82
+ Student.plugin :deep_dup
83
+ student.account = Account.new(email: 'mail@makarius.me')
84
+ end
85
+
86
+ it { student_copy.account.pk.should be_nil }
87
+ it { student_copy.account.email.should == 'mail@makarius.me' }
88
+ it { expect { student_copy.save }.to change { Account.count }.to(2) }
89
+ end
90
+
91
+ describe 'duplicating record with many to one association' do
92
+ before do
93
+ Account.plugin :deep_dup
94
+ end
95
+
96
+ let(:account) { Account.create(email: 'mail@makarius.me', student: student) }
97
+ let(:account_copy) { account.deep_dup }
98
+
99
+ it { account_copy.student.pk.should be_nil }
100
+ it { account_copy.student.name.should == 'Macario' }
101
+ it { expect { account_copy.save }.to change { Student.count }.to(2) }
102
+ end
103
+
104
+ describe 'duplicating record with one to one when foreign key is pk' do
105
+ before do
106
+ Student.plugin :deep_dup
107
+ student.profile = Profile.create(bio: 'likes sequel, rides bycicle', student: student)
108
+ end
109
+
110
+ it { student_copy.profile.pk.should be_nil }
111
+ it { student_copy.profile.bio.should == 'likes sequel, rides bycicle' }
112
+ it { expect { student_copy.save }.to change { Profile.count }.to(2) }
113
+ end
114
+
115
+ describe 'duplicating record with many to one association when foreign key is pk' do
116
+ before do
117
+ Profile.plugin :deep_dup
118
+ end
119
+
120
+ let(:profile) { Profile.create(bio: 'likes sequel, rides bycicle', student: student) }
121
+ let(:profile_copy) { profile.deep_dup }
122
+
123
+ it { profile_copy.student.pk.should be_nil }
124
+ it { profile_copy.student.name.should == 'Macario' }
125
+ it { expect { profile_copy.save }.to change { Student.count }.to(2) }
126
+ end
127
+ end
@@ -0,0 +1,26 @@
1
+ require 'bundler/setup'
2
+ Bundler.require
3
+
4
+ $LOAD_PATH.unshift File.dirname(__FILE__)
5
+
6
+ require 'sequel/extensions/migration'
7
+ load 'support/migrations.rb'
8
+
9
+ DB = Sequel.sqlite
10
+ Sequel::Migration.descendants.each { |m| m.apply(DB, :up) }
11
+
12
+
13
+ RSpec.configure do |config|
14
+ config.around :each do |example|
15
+ load 'support/models.rb'
16
+
17
+ DB.transaction do
18
+ example.run
19
+ raise Sequel::Rollback
20
+ end
21
+
22
+ %w(Program Course Assignment Student Account Profile Enrollment Category).each do |const|
23
+ Object.send :remove_const, const
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,55 @@
1
+ Sequel.migration do
2
+ up do
3
+ create_table :programs do
4
+ primary_key :id
5
+ String :name, null: false
6
+ end
7
+
8
+ create_table :courses do
9
+ primary_key :id
10
+ foreign_key :program_id, :programs, :on_delete => :restrict, :on_update => :restrict, :null => false
11
+ String :name, null: false
12
+ Date :starts
13
+ Date :ends
14
+ end
15
+
16
+ create_table :assignments do
17
+ primary_key :id
18
+ foreign_key :course_id, :courses, :on_delete => :restrict, :on_update => :restrict, :null => false
19
+ String :name, null: false
20
+ end
21
+
22
+ create_table :students do
23
+ primary_key :id
24
+ String :name, null: false
25
+ end
26
+
27
+ create_table :accounts do
28
+ primary_key :id
29
+ foreign_key :student_id, :students, :on_delete => :restrict, :on_update => :restrict, :null => false
30
+ String :email, null: false
31
+ end
32
+
33
+ create_table :profiles do
34
+ foreign_key :student_id, :students, :on_delete => :restrict, :on_update => :restrict, :null => false
35
+ String :bio
36
+ primary_key :student_id
37
+ end
38
+
39
+ create_table :enrollments do
40
+ foreign_key :student_id, :on_delete => :restrict, :on_update => :restrict, :null => false
41
+ foreign_key :course_id, :on_delete => :restrict, :on_update => :restrict, :null => false
42
+ primary_key [:student_id, :course_id]
43
+ end
44
+
45
+ create_table :categories do
46
+ primary_key :id
47
+ String :name, null: false
48
+ end
49
+
50
+ create_table :course_categories do
51
+ foreign_key :course_id, :courses, :on_delete => :restrict, :on_update => :restrict, :null => false
52
+ foreign_key :category_id, :categories, :on_delete => :restrict, :on_update => :restrict, :null => false
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,39 @@
1
+ class Program < Sequel::Model
2
+ one_to_many :courses
3
+ end
4
+
5
+ class Course < Sequel::Model
6
+ many_to_one :program
7
+ one_to_many :assignments
8
+ many_to_many :categories, join_table: :course_categories
9
+ end
10
+
11
+ class Assignment < Sequel::Model
12
+ many_to_one :course
13
+ end
14
+
15
+ class Student < Sequel::Model
16
+ one_to_many :enrollments
17
+ one_to_one :account
18
+ one_to_one :profile
19
+ end
20
+
21
+ class Account < Sequel::Model
22
+ many_to_one :student
23
+ end
24
+
25
+ class Profile < Sequel::Model
26
+ set_primary_key :student_id
27
+ many_to_one :student
28
+ end
29
+
30
+ class Enrollment < Sequel::Model
31
+ unrestrict_primary_key
32
+ set_primary_key [:student_id, :course_id]
33
+ many_to_one :student
34
+ many_to_one :course
35
+ end
36
+
37
+ class Category < Sequel::Model
38
+ many_to_many :courses, join_table: :course_categories
39
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sequel_deep_dup
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - macario
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Makes deep copies of existing sequel models into a new record, along
42
+ with its association tree
43
+ email:
44
+ - mail@makarius.me
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - .gitignore
50
+ - .rspec
51
+ - Gemfile
52
+ - LICENSE.txt
53
+ - README.md
54
+ - Rakefile
55
+ - lib/sequel_deep_dup.rb
56
+ - lib/sequel_deep_dup/version.rb
57
+ - sequel_deep_dup.gemspec
58
+ - spec/deep_dup_spec.rb
59
+ - spec/spec_helper.rb
60
+ - spec/support/migrations.rb
61
+ - spec/support/models.rb
62
+ homepage: ''
63
+ licenses:
64
+ - MIT
65
+ metadata: {}
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubyforge_project:
82
+ rubygems_version: 2.2.2
83
+ signing_key:
84
+ specification_version: 4
85
+ summary: Makes deep copies of existing sequel models into a new record, along with
86
+ its association tree
87
+ test_files:
88
+ - spec/deep_dup_spec.rb
89
+ - spec/spec_helper.rb
90
+ - spec/support/migrations.rb
91
+ - spec/support/models.rb
92
+ has_rdoc: