model_sync 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ lib/model_sync.rb
2
+ Rakefile
3
+ README.rdoc
4
+ Manifest
@@ -0,0 +1,40 @@
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
+ If you've not already added github as a source for gem then do so:
31
+
32
+ gem sources -a http://gems.github.com
33
+
34
+ Now you can install the gem:
35
+
36
+ sudo gem install antonjenkins-model_sync
37
+
38
+ == Limitations
39
+
40
+ 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.
@@ -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 }
@@ -0,0 +1,77 @@
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
+ end
22
+ end
23
+
24
+ def sync_changes
25
+ # If we can find a slave instance...
26
+ if slave_instance = find_slave_instance
27
+ # ... then sync the changes over
28
+ perform_sync(slave_instance)
29
+ end
30
+ end
31
+
32
+ def method_missing(id, *args, &block)
33
+ case(id.to_s)
34
+ when /^create_#{self.class.slave_model_name}$/
35
+ # Only create a new slave if one doesn't already exist
36
+ unless find_slave_instance
37
+ # If we've received a create_{slave_model} call then create a new instance of it and sync to it
38
+ new_instance = self.class.slave_model_class.new
39
+ perform_sync(new_instance)
40
+ # Save the new instance so that its primary key is generated and pass this value onto our master model
41
+ new_instance.save
42
+ self.update_attribute(self.class.relationship.keys.first, new_instance.read_attribute(self.class.relationship.values.first.to_s))
43
+ end
44
+ when /^synch?ed_with_#{self.class.slave_model_name}\?$/
45
+ !!find_slave_instance
46
+ else
47
+ super
48
+ end
49
+ end
50
+
51
+ private
52
+ def find_slave_instance
53
+ # return nil if we don't have a value for the foreign key
54
+ return nil unless foreign_key_value = self.read_attribute(self.class.relationship.keys.first)
55
+ # find the instance of the slave class using the relationship hash
56
+ self.class.slave_model_class.find(:first,
57
+ :conditions => "#{self.class.relationship.values.first.to_s} = #{foreign_key_value}")
58
+ end
59
+
60
+ def perform_sync(slave_instance)
61
+ # Update all the attributes which we've mapped
62
+ self.class.mappings.each do |source, dest|
63
+ slave_instance.update_attribute(dest, self.read_attribute(source))
64
+ end
65
+ # Call the mapping_block if one is supplied
66
+ self.class.mapping_block.call(self, slave_instance) if self.class.mapping_block
67
+ end
68
+ end
69
+ end
70
+
71
+ if defined?(::ActiveRecord)
72
+ module ::ActiveRecord
73
+ class Base
74
+ include ModelSync::Base
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{model_sync}
5
+ s.version = "0.1.4"
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{Sync changes to an ActiveRecord model to another model}
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,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: model_sync
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.4
5
+ platform: ruby
6
+ authors:
7
+ - Anton Jenkins
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-20 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Sync changes to an ActiveRecord model to another model
17
+ email: info@pixellatedvisions.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - lib/model_sync.rb
24
+ - README.rdoc
25
+ files:
26
+ - lib/model_sync.rb
27
+ - Rakefile
28
+ - README.rdoc
29
+ - Manifest
30
+ - model_sync.gemspec
31
+ has_rdoc: true
32
+ homepage: http://github.com/antonjenkins/model_sync
33
+ licenses: []
34
+
35
+ post_install_message:
36
+ rdoc_options:
37
+ - --line-numbers
38
+ - --inline-source
39
+ - --title
40
+ - Model_sync
41
+ - --main
42
+ - README.rdoc
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "1.2"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project: model_sync
60
+ rubygems_version: 1.3.5
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: Sync changes to an ActiveRecord model to another model
64
+ test_files: []
65
+