tricycle-model_sync 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +4 -0
- data/README.rdoc +51 -0
- data/Rakefile +14 -0
- data/lib/model_sync.rb +83 -0
- data/model_sync.gemspec +30 -0
- metadata +73 -0
data/Manifest
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
= model_sync
|
2
|
+
|
3
|
+
model_sync is a simple gem for pushing changes to one of your rails models to another.
|
4
|
+
|
5
|
+
class User < ActiveRecord::Base
|
6
|
+
model_sync :sync_to => :student,
|
7
|
+
:relationship => { :student_id => :s_stno },
|
8
|
+
:mappings => { :forename => :s_forename },
|
9
|
+
:mapping_block => Proc.new do |master, slave|
|
10
|
+
slave.update_attribute(:s_sex, master.gender ? master.gender.code : nil)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
Assuming we have a Student model, the above code will add an after_save callback to the User model which will push changes to the forename value into the s_forename value of Student. The correct Student instance is found using the :relationship hash - student_id in User must equal s_stno in Student.
|
15
|
+
|
16
|
+
Although the above example only pushes changes to forename, you can add more mappings to the :mappings hash to push over as many values as you like.
|
17
|
+
|
18
|
+
When things are a bit more complicated than a simple like for like mapping you can use the :mapping_block option to pass a block which has access to the master and slave objects. Using these objects you can perform more complicated tasks than just making one value equal another.
|
19
|
+
|
20
|
+
== Why use it?
|
21
|
+
|
22
|
+
I developed this gem as part of a web front end which I'm building for an old legacy system. The legacy system is a student records system which holds students which are booked on courses. However I don't want to add users of the website into the system as students until they have actually booked a course. So there needs to be a certain amount of separation between the online element and the legacy system, mainly because I can't make changes to the database structure of the legacy system.
|
23
|
+
|
24
|
+
Once a user books a course and becomes a student as well we've got a user and a student record we need to keep in sync because the user could change their address through the website, for example, which then needs feeding into the main system.
|
25
|
+
|
26
|
+
The model_sync gem will do all the syncing for me in a totally transparent way.
|
27
|
+
|
28
|
+
== Installation
|
29
|
+
|
30
|
+
This gem lives at gemcutter.org (which is amazing by the way!). To use gemcutter is simplicity itself:
|
31
|
+
|
32
|
+
sudo gem install gemcutter
|
33
|
+
sudo gem tumble
|
34
|
+
|
35
|
+
And now with gemcutter as your preferred gem source you can type:
|
36
|
+
|
37
|
+
sudo gem install model_sync
|
38
|
+
|
39
|
+
== Limitations
|
40
|
+
|
41
|
+
Currently the changes are pushed over with an after_save hook using update_attribute on the :sync_to model. This bypasses validations and any failures on update_attribute are currently not handled.
|
42
|
+
|
43
|
+
Also the instance methods which model_sync adds to a class are not available through associations. So if we did something like...
|
44
|
+
|
45
|
+
post.user.synced_with_student?
|
46
|
+
|
47
|
+
... it won't work because we are going through an association which actually uses a proxy behind the scenes. Instead you need to force the instantiation of the target:
|
48
|
+
|
49
|
+
post.user.target.synced_with_student?
|
50
|
+
|
51
|
+
I'm not totally sure how to get around that yet. It's something to do with an ActiveRecord association proxy being used instead of an actual instance of the target class (in this case the User class). If anyone knows how to fix that then please feel free to fork the project and patch.
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'echoe'
|
4
|
+
|
5
|
+
Echoe.new('model_sync', '0.1.4') do |p|
|
6
|
+
p.description = "Sync changes to an ActiveRecord model to another model"
|
7
|
+
p.url = "http://github.com/antonjenkins/model_sync"
|
8
|
+
p.author = "Anton Jenkins"
|
9
|
+
p.email = "info@pixellatedvisions.com"
|
10
|
+
p.ignore_pattern = ["tmp/*", "script/*"]
|
11
|
+
p.development_dependencies = []
|
12
|
+
end
|
13
|
+
|
14
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
data/lib/model_sync.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
module ModelSync
|
2
|
+
module Base
|
3
|
+
def self.included(klass)
|
4
|
+
klass.class_eval do
|
5
|
+
extend Config
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module Config
|
10
|
+
attr_reader :slave_model_name, :slave_model_class, :relationship, :mappings, :mapping_block
|
11
|
+
|
12
|
+
def model_sync(options)
|
13
|
+
@slave_model_name = options[:sync_to].to_s.downcase
|
14
|
+
@slave_model_class = Kernel.const_get(@slave_model_name.classify)
|
15
|
+
@relationship = options[:relationship]
|
16
|
+
@mappings = options[:mappings]
|
17
|
+
@mapping_block = options[:mapping_block]
|
18
|
+
|
19
|
+
# Add a callback to sync_changes on every save
|
20
|
+
self.after_save :sync_changes
|
21
|
+
self.after_destroy :destroy_slave
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def sync_changes
|
26
|
+
# If we can find a slave instance...
|
27
|
+
if slave_instance = find_slave_instance
|
28
|
+
# ... then sync the changes over
|
29
|
+
perform_sync(slave_instance)
|
30
|
+
slave_instance.save
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def method_missing(id, *args, &block)
|
35
|
+
case(id.to_s)
|
36
|
+
when /^create_#{self.class.slave_model_name}$/
|
37
|
+
# Only create a new slave if one doesn't already exist
|
38
|
+
unless find_slave_instance
|
39
|
+
# If we've received a create_{slave_model} call then create a new instance of it and sync to it
|
40
|
+
new_instance = self.class.slave_model_class.new
|
41
|
+
perform_sync(new_instance)
|
42
|
+
new_instance.id = self.id
|
43
|
+
new_instance.save
|
44
|
+
end
|
45
|
+
when /^synch?ed_with_#{self.class.slave_model_name}\?$/
|
46
|
+
!!find_slave_instance
|
47
|
+
else
|
48
|
+
super
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def find_slave_instance
|
54
|
+
# return nil if we don't have a value for the foreign key
|
55
|
+
return nil unless foreign_key_value = self.read_attribute(self.class.relationship.keys.first)
|
56
|
+
# find the instance of the slave class using the relationship hash
|
57
|
+
self.class.slave_model_class.find(:first,
|
58
|
+
:conditions => "#{self.class.relationship.values.first.to_s} = #{foreign_key_value}")
|
59
|
+
end
|
60
|
+
|
61
|
+
def perform_sync(slave_instance)
|
62
|
+
# Update all the attributes which we've mapped
|
63
|
+
self.class.mappings.each do |source, dest|
|
64
|
+
slave_instance.write_attribute(dest, self.read_attribute(source))
|
65
|
+
end
|
66
|
+
# Call the mapping_block if one is supplied
|
67
|
+
self.class.mapping_block.call(self, slave_instance) if self.class.mapping_block
|
68
|
+
end
|
69
|
+
|
70
|
+
def destroy_slave
|
71
|
+
return unless find_slave_instance
|
72
|
+
find_slave_instance.destroy
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
if defined?(::ActiveRecord)
|
78
|
+
module ::ActiveRecord
|
79
|
+
class Base
|
80
|
+
include ModelSync::Base
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/model_sync.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{tricycle-model_sync}
|
5
|
+
s.version = "0.2.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Anton Jenkins"]
|
9
|
+
s.date = %q{2009-07-20}
|
10
|
+
s.description = %q{Fork of the model_sync gem by Anton Jenkins}
|
11
|
+
s.email = %q{info@pixellatedvisions.com}
|
12
|
+
s.extra_rdoc_files = ["lib/model_sync.rb", "README.rdoc"]
|
13
|
+
s.files = ["lib/model_sync.rb", "Rakefile", "README.rdoc", "Manifest", "model_sync.gemspec"]
|
14
|
+
s.homepage = %q{http://github.com/antonjenkins/model_sync}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Model_sync", "--main", "README.rdoc"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{model_sync}
|
18
|
+
s.rubygems_version = %q{1.3.4}
|
19
|
+
s.summary = %q{Sync changes to an ActiveRecord model to another model}
|
20
|
+
|
21
|
+
if s.respond_to? :specification_version then
|
22
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
23
|
+
s.specification_version = 3
|
24
|
+
|
25
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
26
|
+
else
|
27
|
+
end
|
28
|
+
else
|
29
|
+
end
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tricycle-model_sync
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Anton Jenkins
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2009-07-20 00:00:00 +10:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Fork of the model_sync gem by Anton Jenkins
|
22
|
+
email: info@pixellatedvisions.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- lib/model_sync.rb
|
29
|
+
- README.rdoc
|
30
|
+
files:
|
31
|
+
- lib/model_sync.rb
|
32
|
+
- Rakefile
|
33
|
+
- README.rdoc
|
34
|
+
- Manifest
|
35
|
+
- model_sync.gemspec
|
36
|
+
has_rdoc: true
|
37
|
+
homepage: http://github.com/antonjenkins/model_sync
|
38
|
+
licenses: []
|
39
|
+
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options:
|
42
|
+
- --line-numbers
|
43
|
+
- --inline-source
|
44
|
+
- --title
|
45
|
+
- Model_sync
|
46
|
+
- --main
|
47
|
+
- README.rdoc
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
segments:
|
55
|
+
- 0
|
56
|
+
version: "0"
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
segments:
|
62
|
+
- 1
|
63
|
+
- 2
|
64
|
+
version: "1.2"
|
65
|
+
requirements: []
|
66
|
+
|
67
|
+
rubyforge_project: model_sync
|
68
|
+
rubygems_version: 1.3.6
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: Sync changes to an ActiveRecord model to another model
|
72
|
+
test_files: []
|
73
|
+
|