has_alter_ego 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # has_alter_ego
2
+
3
+ has_alter_ego makes it possible to keep seed and live data transparently in parallel. In contrast to other seed
4
+ data approaches has_alter_ego synchronizes the seed definitions with your database objects automagically unless you've
5
+ overridden it in the database.
6
+
7
+ # Installation
8
+
9
+ ## Rails 2.3.x
10
+ ### As a plugin
11
+ script/plugin install git://github.com/aduffeck/has_alter_ego.git
12
+ script/generate has_alter_ego
13
+ rake db:migrate
14
+
15
+ ### As a gem
16
+ Add the following line to your config/environment.rb file:
17
+ config.gem "has_alter_ego"
18
+ Then
19
+ gem install has_alter_ego
20
+ script/generate has_alter_ego
21
+ rake db:migrate
22
+
23
+ # Usage
24
+
25
+ The seed data is defined in YAML files called after the model's table. The files are expected in db/fixtures/alter_egos.
26
+
27
+ Say you have a Model Car. has_alter_ego is enabled with the has_alter_ego method:
28
+
29
+ create_table :cars do |t|
30
+ t.string :brand
31
+ t.string :model
32
+ end
33
+
34
+
35
+ class Car < ActiveRecord::Base
36
+ has_alter_ego
37
+ end
38
+
39
+ You would then create a file db/fixtures/has_alter_ego/cars.yml with the seed data:
40
+
41
+ 1:
42
+ brand: Lotus
43
+ model: Elise
44
+
45
+ 2:
46
+ brand: Porsche
47
+ model: 911
48
+
49
+ 3:
50
+ brand: Ferrari
51
+ model: F50
52
+
53
+ 4:
54
+ brand: Corvette
55
+ model: C5
56
+
57
+ and you'd automagically have those objects available in your database.
58
+
59
+ Car.find(1)
60
+ => #<Car id: 1, brand: "Lotus", model: "Elise">
61
+
62
+ Whenever the seed definition changes the objects in the database inherit the changes unless they have been overridden.
63
+ You can check if an object was created from seed definition with *has_alter_ego?*:
64
+
65
+ @car = Car.find(1)
66
+ @car.has_alter_ego?
67
+ => true
68
+
69
+ Car.new.has_alter_ego?
70
+ => false
71
+
72
+ The method *alter_ego_state* tells whether an object has been overridden. "modified" objects will no longer inherit
73
+ changes to the seed data.
74
+
75
+ @car.alter_ego_state
76
+ => "default"
77
+
78
+ @car.update_attribute(:model, "foo")
79
+ => true
80
+ @car
81
+ => #<Car id: 1, brand: "Lotus", model: "foo">
82
+ @car.alter_ego_state
83
+ => "modified"
84
+
85
+ If you don't want to inherit changes for an object without actually modifying it you can use *pin!*:
86
+
87
+ @car.pin!
88
+ => true
89
+ @car.alter_ego_state
90
+ => "pinned"
91
+
92
+
93
+ *reset* reverts the changes in the database and activates the synchronization again:
94
+ @car.reset
95
+ => #<Car id: 1, brand: "Lotus", model: "Elise">
96
+ @car.alter_ego_state
97
+ => "default"
98
+
99
+
100
+ Copyright (c) 2010 André Duffeck, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require "rake/gempackagetask"
5
+
6
+ desc 'Default: run unit tests.'
7
+ task :default => :test
8
+
9
+ desc 'Test the has_alter_ego plugin.'
10
+ Rake::TestTask.new(:test) do |t|
11
+ t.libs << 'lib'
12
+ t.libs << 'test'
13
+ t.pattern = 'test/**/*_test.rb'
14
+ t.verbose = true
15
+ end
16
+
17
+ desc 'Generate documentation for the has_alter_ego plugin.'
18
+ Rake::RDocTask.new(:rdoc) do |rdoc|
19
+ rdoc.rdoc_dir = 'rdoc'
20
+ rdoc.title = 'Schizophrenia'
21
+ rdoc.options << '--line-numbers' << '--inline-source'
22
+ rdoc.rdoc_files.include('README')
23
+ rdoc.rdoc_files.include('lib/**/*.rb')
24
+ end
25
+
26
+ Rake::GemPackageTask.new(eval(File.read("has_alter_ego.gemspec"))) { |pkg| }
@@ -0,0 +1,8 @@
1
+ class HasAlterEgoGenerator < Rails::Generator::Base
2
+ def manifest
3
+ record do |m|
4
+ m.directory('db/fixtures/alter_egos')
5
+ m.migration_template('create_alter_egos.rb', "db/migrate", :migration_file_name => 'create_alter_egos')
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,14 @@
1
+ class CreateAlterEgos < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :alter_egos do |t|
4
+ t.string :alter_ego_object_id
5
+ t.string :alter_ego_object_type, :limit => 40
6
+ t.string :state
7
+ end
8
+ add_index :alter_egos, [:alter_ego_object_id, :alter_ego_object_type]
9
+ end
10
+
11
+ def self.down
12
+ drop_table :alter_egos
13
+ end
14
+ end
@@ -0,0 +1,112 @@
1
+ require File.join(File.dirname(__FILE__), "has_alter_ego", "alter_ego")
2
+
3
+ module HasAlterEgo
4
+ module ActiveRecordAdapater
5
+ def self.included(base)
6
+ base.send :extend, ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+ def has_alter_ego opts = {}
11
+ opts.reverse_merge!({:reserved_space => 1000})
12
+
13
+ class_eval do
14
+ has_one :alter_ego, :as => :alter_ego_object
15
+ alias_method :save_without_alter_ego, :save
16
+ send :include, InstanceMethods
17
+ reserve_space(opts[:reserved_space])
18
+ parse_yml
19
+ end
20
+ end
21
+
22
+ # Reserve the first n IDs for stubbed objects
23
+ def reserve_space space
24
+ return unless self.columns_hash[self.primary_key].klass == Fixnum
25
+ return if self.last and self.last[self.primary_key] >= space
26
+
27
+ o = self.new
28
+ o[self.primary_key] = space
29
+ o.save_without_alter_ego
30
+ o.destroy
31
+ return
32
+ end
33
+
34
+ def parse_yml
35
+ yml = get_yml
36
+ yml.keys.each do |o|
37
+ parse_yml_for o
38
+ end
39
+ yml
40
+ end
41
+
42
+ def parse_yml_for primary_key
43
+ yml = get_yml
44
+ db_object = self.find(primary_key) rescue nil
45
+ if db_object
46
+ raise "There is already a #{db_object.class} with id #{db_object.id} in the database." unless db_object.has_alter_ego?
47
+ if db_object.alter_ego.state == 'default'
48
+ yml[primary_key].keys.each do |attr|
49
+ db_object[attr] = yml[primary_key][attr]
50
+ end
51
+ db_object.save_without_alter_ego
52
+ end
53
+ else
54
+ db_object = self.new
55
+ db_object[self.primary_key] = primary_key
56
+ yml[primary_key].keys.each do |attr|
57
+ db_object[attr] = yml[primary_key][attr]
58
+ end
59
+ db_object.build_alter_ego
60
+ db_object.alter_ego.state = 'default'
61
+ db_object.save_without_alter_ego
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def get_yml
68
+ filename = File.join(RAILS_ROOT, "db", "fixtures", "alter_egos", self.table_name + ".yml")
69
+ return {} unless File.exists?(filename)
70
+ yml = File.open(filename) do |yf|
71
+ YAML::load( yf )
72
+ end
73
+ yml
74
+ end
75
+ end
76
+
77
+ module InstanceMethods
78
+ def has_alter_ego?
79
+ return self.alter_ego.present?
80
+ end
81
+
82
+ def alter_ego_state
83
+ self.alter_ego.state if self.alter_ego
84
+ end
85
+
86
+ def save perform_validation = true
87
+ if self.alter_ego
88
+ self.alter_ego.state = 'modified'
89
+ self.alter_ego.save
90
+ end
91
+ save_without_alter_ego perform_validation
92
+ end
93
+
94
+ def pin!
95
+ self.alter_ego.state = 'pinned'
96
+ self.alter_ego.save
97
+ end
98
+
99
+ def reset
100
+ self.alter_ego.state = 'default'
101
+ self.alter_ego.save
102
+
103
+ self.class.parse_yml_for self[self.class.primary_key]
104
+ self.reload
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ class ActiveRecord::Base
111
+ include HasAlterEgo::ActiveRecordAdapater
112
+ end
@@ -0,0 +1,3 @@
1
+ class AlterEgo < ActiveRecord::Base
2
+ belongs_to :alter_ego_object, :polymorphic => true
3
+ end
@@ -0,0 +1 @@
1
+
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'has_alter_ego'
@@ -0,0 +1,2 @@
1
+ 1:
2
+ brand: Hercules
@@ -0,0 +1,15 @@
1
+ 1:
2
+ brand: Lotus
3
+ model: Elise
4
+
5
+ 2:
6
+ brand: Porsche
7
+ model: 911
8
+
9
+ 3:
10
+ brand: Ferrari
11
+ model: F50
12
+
13
+ 4:
14
+ brand: Corvette
15
+ model: C5
@@ -0,0 +1,5 @@
1
+ water:
2
+ color: none
3
+
4
+ coffee:
5
+ color: black
@@ -0,0 +1,156 @@
1
+ require 'test_helper'
2
+
3
+ class Car < ActiveRecord::Base
4
+ has_many :tires
5
+ has_alter_ego
6
+ end
7
+
8
+ class Bike < ActiveRecord::Base
9
+ end
10
+
11
+ class Scooter < ActiveRecord::Base
12
+ end
13
+
14
+ class Tire < ActiveRecord::Base
15
+ end
16
+
17
+ class Drink < ActiveRecord::Base
18
+ set_primary_key :name
19
+ has_alter_ego
20
+ end
21
+
22
+ class HasAlterEgoTest < Test::Unit::TestCase
23
+ def test_space_gets_reserved
24
+ c = Car.create
25
+ assert_equal 1001, c.id
26
+
27
+ assert_equal 1, Scooter.create.id
28
+ Scooter.has_alter_ego :reserved_space => 500
29
+ assert_equal 501, Scooter.create.id
30
+ end
31
+
32
+ def test_has_alter_ego?
33
+ assert Car.find(1).alter_ego
34
+ assert !Car.new.has_alter_ego?
35
+ end
36
+
37
+ def test_create_objects_from_yml
38
+ assert_equal 4, Car.count
39
+ c1 = Car.find(1)
40
+ assert_equal "Lotus", c1.brand
41
+ assert_equal "Elise", c1.model
42
+ assert c1.has_alter_ego?
43
+ end
44
+
45
+ def test_exception_is_raised_if_id_is_already_in_use
46
+ bike = Bike.create
47
+ assert_raise RuntimeError do
48
+ Bike.has_alter_ego
49
+ end
50
+ end
51
+
52
+ def test_object_attributes_are_updated_if_not_modified
53
+ c = Car.find(1)
54
+ assert_equal "Lotus", c.brand
55
+ assert_equal "default", c.alter_ego_state
56
+
57
+ c.brand = "Toyota"
58
+ c.save_without_alter_ego
59
+ c.reload
60
+ assert_equal "Toyota", c.brand
61
+ assert_equal "default", c.alter_ego_state
62
+
63
+ Car.parse_yml
64
+ c.reload
65
+ assert_equal "Lotus", c.brand
66
+ end
67
+
68
+ def test_object_attributes_are_not_updated_if_pinned
69
+ c = Car.find(2)
70
+ c.reset
71
+ assert_equal "Porsche", c.brand
72
+
73
+ c.brand = "VW"
74
+ c.save_without_alter_ego
75
+ c.pin!
76
+ c.reload
77
+ assert_equal "VW", c.brand
78
+ assert_equal "pinned", c.alter_ego_state
79
+
80
+ Car.parse_yml
81
+ c.reload
82
+ assert_equal "VW", c.brand
83
+ end
84
+
85
+ def test_object_attributes_are_not_updated_if_modified
86
+ c = Car.find(2)
87
+ assert_equal "Porsche", c.brand
88
+
89
+ c.brand = "VW"
90
+ c.save
91
+ c.reload
92
+ assert_equal "VW", c.brand
93
+ assert_equal "modified", c.alter_ego_state
94
+
95
+ Car.parse_yml
96
+ c.reload
97
+ assert_equal "VW", c.brand
98
+ end
99
+
100
+ def test_reset
101
+ c = Car.find(3)
102
+ assert_equal "Ferrari", c.brand
103
+ assert_equal "default", c.alter_ego_state
104
+
105
+ c.brand = "Fiat"
106
+ c.save
107
+ c.reload
108
+
109
+ assert_equal "Fiat", c.brand
110
+ assert_equal "modified", c.alter_ego_state
111
+ c.reset
112
+ c.reload
113
+
114
+ assert_equal "Ferrari", c.brand
115
+ assert_equal "default", c.alter_ego_state
116
+ end
117
+
118
+ def test_associations_do_not_change_state
119
+ c = Car.find(4)
120
+ assert_equal "default", c.alter_ego_state
121
+ assert_equal 0, c.tires.size
122
+
123
+ 4.times do
124
+ c.tires << Tire.new
125
+ end
126
+ c.reload
127
+ assert_equal "default", c.alter_ego_state
128
+ assert_equal 4, c.tires.size
129
+ end
130
+
131
+ def test_different_primary_key
132
+ assert_equal 2, Drink.all.size
133
+ assert_equal "none", Drink.find("water").color
134
+
135
+ water = Drink.find("water")
136
+ assert water.has_alter_ego?
137
+ assert_equal "default", water.alter_ego_state
138
+
139
+ water.color = "blue"
140
+ water.save
141
+ water.reload
142
+ assert_equal "blue", water.color
143
+ assert_equal "modified", water.alter_ego_state
144
+
145
+ water.reset
146
+ water.reload
147
+ assert_equal "none", water.color
148
+ assert_equal "default", water.alter_ego_state
149
+
150
+ orangejuice = Drink.new
151
+ orangejuice.name = "orangejuice"
152
+ orangejuice.color = "yellow"
153
+ orangejuice.save
154
+ assert !orangejuice.has_alter_ego?
155
+ end
156
+ end
@@ -0,0 +1,36 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+ require 'active_support/test_case'
4
+ require 'active_record'
5
+ require 'test/unit'
6
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
7
+
8
+ require File.dirname(__FILE__) + '/../lib/has_alter_ego'
9
+ RAILS_ROOT = File.dirname(__FILE__)
10
+
11
+ silence_stream(STDOUT) do
12
+ ActiveRecord::Schema.define do
13
+ create_table :alter_egos do |t|
14
+ t.string :alter_ego_object_id
15
+ t.string :alter_ego_object_type, :limit => 40
16
+ t.string :state
17
+ end
18
+
19
+ create_table :cars do |t|
20
+ t.string :brand
21
+ t.string :model
22
+ end
23
+
24
+ create_table :bikes
25
+ create_table :scooters
26
+
27
+ create_table :tires do |t|
28
+ t.integer :car_id
29
+ end
30
+
31
+ create_table :drinks, :id => false do |t|
32
+ t.string :name
33
+ t.string :color
34
+ end
35
+ end
36
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: has_alter_ego
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - "Andr\xC3\xA9 Duffeck"
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-06-09 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: " has_alter_ego makes it possible to keep seed and live data transparently in parallel. In contrast to other seed\n data approaches has_alter_ego synchronizes the seed definitions with your database objects automagically unless you've\n overridden it in the database.\n"
23
+ email:
24
+ - aduffeck@suse.de
25
+ executables: []
26
+
27
+ extensions: []
28
+
29
+ extra_rdoc_files: []
30
+
31
+ files:
32
+ - lib/has_alter_ego/alter_ego.rb
33
+ - lib/has_alter_ego.rb
34
+ - lib/tasks/has_alter_ego.rake
35
+ - README.md
36
+ - Rakefile
37
+ - rails/init.rb
38
+ - generators/has_alter_ego/templates/create_alter_egos.rb
39
+ - generators/has_alter_ego/has_alter_ego_generator.rb
40
+ - test/db/fixtures/alter_egos/bikes.yml
41
+ - test/db/fixtures/alter_egos/cars.yml
42
+ - test/db/fixtures/alter_egos/drinks.yml
43
+ - test/test_helper.rb
44
+ - test/has_alter_ego_test.rb
45
+ has_rdoc: false
46
+ homepage: http://github.com/aduffeck/has_alter_ego
47
+ licenses: []
48
+
49
+ post_install_message:
50
+ rdoc_options: []
51
+
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ hash: 3
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.3.7
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: A Rails plugin which makes it possible to keep seed and live data in parallel
79
+ test_files:
80
+ - test/has_alter_ego_test.rb