has_alter_ego 0.0.1

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.
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