ar-octopus 0.0.19 → 0.0.20

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,2 +1,3 @@
1
1
  pkg/*.*
2
2
  tmp/*
3
+ *.sqlite3
@@ -1,9 +1,9 @@
1
1
  <h1> Octopus - Easy Database Sharding for ActiveRecord</h1>
2
2
 
3
- <p> Octopus is a better way to do Database Sharding in ActiveRecord. Sharding allows multiple databases in the same rails application. While there are several projects that implement Sharding (e.g. DbCharmer, DataFabric), each project has its own limitations. The main goal of octopus project is to provide a nice and clean way of doing Database Sharding.</p>
3
+ <p> Octopus is a better way to do Database Sharding in ActiveRecord. Sharding allows multiple databases in the same rails application. While there are several projects that implement Sharding (e.g. DbCharmer, DataFabric, MultiDb), each project has its own limitations. The main goal of octopus project is to provide a nice and clean way of doing Database Sharding.</p>
4
4
 
5
5
  <h2>Feature list: </h2>
6
- <p> The design of the api is made to be simple as possible. Octopus is focusing in the end user, giving the power of multiple databases, but with reliable code and flexibility. Octopus is focused on Rails 3, but will be soon compatible with Rails 2.x.</p>
6
+ <p> The design of the api is made to be simple as possible. Octopus is focusing in the end user, giving the power of multiple databases, but with reliable code and flexibility. Octopus is focused on Rails 3, is compatible with Rails 2.x.</p>
7
7
 
8
8
  <p> Octopus supports: </p>
9
9
 
data/Rakefile CHANGED
@@ -29,7 +29,7 @@ begin
29
29
  gem.authors = ["Thiago Pradi", "Mike Perham", "Amit Agarwal"]
30
30
  gem.add_development_dependency "rspec", ">= 1.2.9"
31
31
  gem.add_dependency('activerecord', '>= 3.0.0beta')
32
- gem.version = "0.0.19"
32
+ gem.version = "0.0.20"
33
33
  end
34
34
  Jeweler::GemcutterTasks.new
35
35
  rescue LoadError
@@ -90,7 +90,7 @@ namespace :db do
90
90
  Dir.chdir(File.expand_path(File.dirname(__FILE__) + "/spec"))
91
91
  require "database_connection"
92
92
  require "octopus"
93
- [:master, :brazil, :canada, :russia, :alone_shard, :postgresql_shard].each do |shard_symbol|
93
+ [:master, :brazil, :canada, :russia, :alone_shard, :postgresql_shard, :sqlite_shard].each do |shard_symbol|
94
94
  ActiveRecord::Base.using(shard_symbol).connection.initialize_schema_migrations_table()
95
95
 
96
96
  ActiveRecord::Base.using(shard_symbol).connection.create_table(:users) do |u|
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{ar-octopus}
8
- s.version = "0.0.19"
8
+ s.version = "0.0.20"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Thiago Pradi", "Mike Perham", "Amit Agarwal"]
12
- s.date = %q{2010-07-05}
12
+ s.date = %q{2010-07-07}
13
13
  s.description = %q{This gem allows you to use sharded databases with ActiveRecord. this also provides a interface for replication and for running migrations with multiples shards.}
14
14
  s.email = %q{tchandy@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
25
25
  "doc/features.textile",
26
26
  "doc/libraries.textile",
27
27
  "doc/shards.yml",
28
+ "init.rb",
28
29
  "lib/octopus.rb",
29
30
  "lib/octopus/association.rb",
30
31
  "lib/octopus/association_collection.rb",
@@ -32,8 +33,11 @@ Gem::Specification.new do |s|
32
33
  "lib/octopus/has_and_belongs_to_many_association.rb",
33
34
  "lib/octopus/migration.rb",
34
35
  "lib/octopus/model.rb",
35
- "lib/octopus/persistence.rb",
36
36
  "lib/octopus/proxy.rb",
37
+ "lib/octopus/rails2/association.rb",
38
+ "lib/octopus/rails2/persistence.rb",
39
+ "lib/octopus/rails3/association.rb",
40
+ "lib/octopus/rails3/persistence.rb",
37
41
  "lib/octopus/scope_proxy.rb",
38
42
  "rails/init.rb",
39
43
  "spec/config/shards.yml",
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), 'rails', 'init')
@@ -35,14 +35,28 @@ module Octopus
35
35
  def self.excluded_enviroments
36
36
  @excluded_enviroments || ['development',"cucumber", "test"]
37
37
  end
38
+
39
+ def self.rails3?
40
+ ActiveRecord::VERSION::MAJOR == 3
41
+ end
38
42
  end
39
43
 
40
- require "octopus/proxy"
41
- require "octopus/migration"
44
+
42
45
  require "octopus/model"
43
- require "octopus/persistence"
44
- require "octopus/controller"
45
- require "octopus/association"
46
+ require "octopus/migration"
46
47
  require "octopus/association_collection"
48
+ require "octopus/has_and_belongs_to_many_association"
49
+ require "octopus/association"
50
+
51
+ if Octopus.rails3?
52
+ require "octopus/rails3/association"
53
+ require "octopus/rails3/persistence"
54
+ else
55
+ require "octopus/rails2/association"
56
+ require "octopus/rails2/persistence"
57
+ end
58
+
59
+ require "octopus/proxy"
47
60
  require "octopus/scope_proxy"
48
- require "octopus/has_and_belongs_to_many_association"
61
+ require "octopus/controller"
62
+
@@ -22,111 +22,6 @@ module Octopus::Association
22
22
  options[:before_remove] = :set_connection
23
23
  end
24
24
  end
25
-
26
- def collection_reader_method(reflection, association_proxy_class)
27
- define_method(reflection.name) do |*params|
28
- force_reload = params.first unless params.empty?
29
- reload_connection()
30
-
31
- association = association_instance_get(reflection.name)
32
-
33
- unless association
34
- association = association_proxy_class.new(self, reflection)
35
- association_instance_set(reflection.name, association)
36
- end
37
-
38
- reflection.klass.uncached { association.reload } if force_reload
39
-
40
- association
41
- end
42
-
43
- define_method("#{reflection.name.to_s.singularize}_ids") do
44
- reload_connection()
45
- if send(reflection.name).loaded? || reflection.options[:finder_sql]
46
- send(reflection.name).map(&:id)
47
- else
48
- if reflection.through_reflection && reflection.source_reflection.belongs_to?
49
- through = reflection.through_reflection
50
- primary_key = reflection.source_reflection.primary_key_name
51
- send(through.name).select("DISTINCT #{through.quoted_table_name}.#{primary_key}").map!(&:"#{primary_key}")
52
- else
53
- send(reflection.name).select("#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").except(:includes).map!(&:id)
54
- end
55
- end
56
- end
57
- end
58
-
59
- def association_constructor_method(constructor, reflection, association_proxy_class)
60
- define_method("#{constructor}_#{reflection.name}") do |*params|
61
- reload_connection()
62
- attributees = params.first unless params.empty?
63
- replace_existing = params[1].nil? ? true : params[1]
64
- association = association_instance_get(reflection.name)
65
-
66
- unless association
67
- association = association_proxy_class.new(self, reflection)
68
- association_instance_set(reflection.name, association)
69
- end
70
-
71
- if association_proxy_class == ActiveRecord::Associations::HasOneAssociation
72
- return_val = association.send(constructor, attributees, replace_existing)
73
- else
74
- return_val = association.send(constructor, attributees)
75
- end
76
-
77
- if should_set_current_shard?
78
- return_val.current_shard = self.current_shard
79
- end
80
-
81
- return_val
82
- end
83
- end
84
-
85
- def association_accessor_methods(reflection, association_proxy_class)
86
- define_method(reflection.name) do |*params|
87
- reload_connection()
88
- force_reload = params.first unless params.empty?
89
- association = association_instance_get(reflection.name)
90
-
91
- if association.nil? || force_reload
92
- association = association_proxy_class.new(self, reflection)
93
- retval = force_reload ? reflection.klass.uncached { association.reload } : association.reload
94
- if retval.nil? and association_proxy_class == ActiveRecord::Associations::BelongsToAssociation
95
- association_instance_set(reflection.name, nil)
96
- return nil
97
- end
98
- association_instance_set(reflection.name, association)
99
- end
100
-
101
- association.target.nil? ? nil : association
102
- end
103
-
104
- define_method("loaded_#{reflection.name}?") do
105
- reload_connection()
106
- association = association_instance_get(reflection.name)
107
- association && association.loaded?
108
- end
109
-
110
- define_method("#{reflection.name}=") do |new_value|
111
- reload_connection()
112
- association = association_instance_get(reflection.name)
113
-
114
- if association.nil? || association.target != new_value
115
- association = association_proxy_class.new(self, reflection)
116
- end
117
-
118
- association.replace(new_value)
119
- association_instance_set(reflection.name, new_value.nil? ? nil : association)
120
- end
121
-
122
- define_method("set_#{reflection.name}_target") do |target|
123
- return if target.nil? and association_proxy_class == ActiveRecord::Associations::BelongsToAssociation
124
- reload_connection()
125
- association = association_proxy_class.new(self, reflection)
126
- association.target = target
127
- association_instance_set(reflection.name, association)
128
- end
129
- end
130
25
  end
131
26
 
132
- ActiveRecord::Base.extend(Octopus::Association)
27
+ ActiveRecord::Base.extend(Octopus::Association)
@@ -1,40 +1,15 @@
1
- class ActiveRecord::Associations::AssociationCollection
1
+ module Octopus::AssociationCollection
2
2
  def should_wrap_the_connection?
3
3
  @owner.respond_to?(:current_shard) && @owner.current_shard != nil
4
4
  end
5
5
 
6
- def count(column_name = nil, options = {})
7
- if @reflection.options[:counter_sql]
8
- if should_wrap_the_connection?
9
- @owner.using(@owner.current_shard) { @reflection.klass.count_by_sql(@counter_sql) }
10
- else
11
- @reflection.klass.count_by_sql(@counter_sql)
12
- end
13
- else
14
- column_name, options = nil, column_name if column_name.is_a?(Hash)
15
-
16
- if @reflection.options[:uniq]
17
- # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
18
- column_name = "#{@reflection.quoted_table_name}.#{@reflection.klass.primary_key}" unless column_name
19
- options.merge!(:distinct => true)
20
- end
21
-
22
- value = @reflection.klass.send(:with_scope, construct_scope) do
23
- if should_wrap_the_connection?
24
- @owner.using(@owner.current_shard) { @reflection.klass.count(column_name, options) }
25
- else
26
- @reflection.klass.count(column_name, options)
27
- end
28
- end
29
-
30
- limit = @reflection.options[:limit]
31
- offset = @reflection.options[:offset]
32
-
33
- if limit || offset
34
- [ [value - offset.to_i, 0].max, limit.to_i ].min
35
- else
36
- value
37
- end
6
+ def count(*args)
7
+ if should_wrap_the_connection?
8
+ @owner.using(@owner.current_shard) { super }
9
+ else
10
+ super
38
11
  end
39
12
  end
40
- end
13
+ end
14
+
15
+ ActiveRecord::Associations::AssociationCollection.send(:include, Octopus::AssociationCollection)
@@ -1,43 +1,21 @@
1
- class ActiveRecord::Associations::HasAndBelongsToManyAssociation
1
+ module Octopus::HasAndBelongsToManyAssociation
2
+ def self.included(base)
3
+ base.instance_eval do
4
+ alias_method_chain :insert_record, :octopus
5
+ end
6
+ end
7
+
2
8
  def should_wrap_the_connection?
3
9
  @owner.respond_to?(:current_shard) && @owner.current_shard != nil
4
10
  end
5
-
6
- def insert_record(record, force = true, validate = true)
7
- if record.new_record?
8
- if force
9
- record.save!
10
- else
11
- return false unless record.save(:validate => validate)
12
- end
13
- end
14
11
 
15
- if @reflection.options[:insert_sql]
16
- @owner.connection.insert(interpolate_sql(@reflection.options[:insert_sql], record))
17
- else
18
- relation = Arel::Table.new(@reflection.options[:join_table])
19
- attributes = columns.inject({}) do |attrs, column|
20
- case column.name.to_s
21
- when @reflection.primary_key_name.to_s
22
- attrs[relation[column.name]] = owner_quoted_id
23
- when @reflection.association_foreign_key.to_s
24
- attrs[relation[column.name]] = record.quoted_id
25
- else
26
- if record.has_attribute?(column.name)
27
- value = @owner.send(:quote_value, record[column.name], column)
28
- attrs[relation[column.name]] = value unless value.nil?
29
- end
30
- end
31
- attrs
32
- end
33
-
34
- if should_wrap_the_connection?
35
- @owner.using(@owner.current_shard) { relation.insert(attributes) }
36
- else
37
- relation.insert(attributes)
38
- end
12
+ def insert_record_with_octopus(record, force = true, validate = true)
13
+ if should_wrap_the_connection?
14
+ @owner.using(@owner.current_shard) { insert_record_without_octopus(record, force, validate) }
15
+ else
16
+ insert_record_without_octopus(record, force, validate)
39
17
  end
40
-
41
- return true
42
18
  end
43
- end
19
+ end
20
+
21
+ ActiveRecord::Associations::HasAndBelongsToManyAssociation.send(:include, Octopus::HasAndBelongsToManyAssociation)
@@ -1,4 +1,10 @@
1
1
  module Octopus::Migration
2
+ def self.extended(base)
3
+ class << base
4
+ alias_method_chain :migrate, :octopus
5
+ end
6
+ end
7
+
2
8
  def using(*args, &block)
3
9
  Octopus.config()
4
10
 
@@ -15,6 +21,7 @@ module Octopus::Migration
15
21
  self.connection().current_shard = args
16
22
  end
17
23
 
24
+
18
25
  yield if block_given?
19
26
 
20
27
  return self
@@ -42,7 +49,17 @@ module Octopus::Migration
42
49
 
43
50
  return self
44
51
  end
45
- end
52
+
53
+ def migrate_with_octopus(direction)
54
+ ret = migrate_without_octopus(direction)
55
+ # This Cleans the proxy
56
+ # TODO - REFACTOR!
57
+ ActiveRecord::Base.connection.instance_variable_set(:@current_shard, :master)
58
+ ActiveRecord::Base.connection.instance_variable_set(:@current_group, nil)
59
+ ActiveRecord::Base.connection().block = false
46
60
 
61
+ return ret
62
+ end
63
+ end
47
64
 
48
- ActiveRecord::Migration.extend(Octopus::Migration)
65
+ ActiveRecord::Migration.extend(Octopus::Migration)
@@ -37,7 +37,13 @@ module Octopus::Model
37
37
  else
38
38
  self.current_shard = self.class.connection_proxy.last_current_shard
39
39
  end
40
- end
40
+ end
41
+
42
+ if !Octopus.rails3?
43
+ def after_initialize
44
+ set_current_shard()
45
+ end
46
+ end
41
47
  end
42
48
 
43
49
  def hijack_connection()
@@ -69,6 +75,10 @@ module Octopus::Model
69
75
  def should_set_current_shard?
70
76
  self.respond_to?(:current_shard) && self.current_shard != nil
71
77
  end
78
+
79
+ def reload_connection()
80
+ set_connection() if should_set_current_shard?
81
+ end
72
82
  end
73
83
 
74
84
  module ClassMethods
@@ -0,0 +1,133 @@
1
+ module Octopus
2
+ module Rails2
3
+ module Association
4
+ def association_accessor_methods(reflection, association_proxy_class)
5
+ define_method(reflection.name) do |*params|
6
+ force_reload = params.first unless params.empty?
7
+ reload_connection()
8
+ association = association_instance_get(reflection.name)
9
+
10
+ if association.nil? || force_reload
11
+ association = association_proxy_class.new(self, reflection)
12
+ retval = force_reload ? reflection.klass.uncached { association.reload } : association.reload
13
+ if retval.nil? and association_proxy_class == ActiveRecord::Associations::BelongsToAssociation
14
+ association_instance_set(reflection.name, nil)
15
+ return nil
16
+ end
17
+ association_instance_set(reflection.name, association)
18
+ end
19
+
20
+ association.target.nil? ? nil : association
21
+ end
22
+
23
+ define_method("loaded_#{reflection.name}?") do
24
+ reload_connection()
25
+ association = association_instance_get(reflection.name)
26
+ association && association.loaded?
27
+ end
28
+
29
+ define_method("#{reflection.name}=") do |new_value|
30
+ association = association_instance_get(reflection.name)
31
+ reload_connection()
32
+ if association.nil? || association.target != new_value
33
+ association = association_proxy_class.new(self, reflection)
34
+ end
35
+
36
+ if association_proxy_class == ActiveRecord::Associations::HasOneThroughAssociation
37
+ association.create_through_record(new_value)
38
+ if new_record?
39
+ association_instance_set(reflection.name, new_value.nil? ? nil : association)
40
+ else
41
+ self.send(reflection.name, new_value)
42
+ end
43
+ else
44
+ association.replace(new_value)
45
+ association_instance_set(reflection.name, new_value.nil? ? nil : association)
46
+ end
47
+ end
48
+
49
+ define_method("set_#{reflection.name}_target") do |target|
50
+ reload_connection()
51
+ return if target.nil? and association_proxy_class == ActiveRecord::Associations::BelongsToAssociation
52
+ association = association_proxy_class.new(self, reflection)
53
+ association.target = target
54
+ association_instance_set(reflection.name, association)
55
+ end
56
+ end
57
+
58
+ def collection_reader_method(reflection, association_proxy_class)
59
+ define_method(reflection.name) do |*params|
60
+ force_reload = params.first unless params.empty?
61
+ reload_connection()
62
+ association = association_instance_get(reflection.name)
63
+
64
+ unless association
65
+ association = association_proxy_class.new(self, reflection)
66
+ association_instance_set(reflection.name, association)
67
+ end
68
+
69
+ reflection.klass.uncached { association.reload } if force_reload
70
+
71
+ association
72
+ end
73
+
74
+ define_method("#{reflection.name.to_s.singularize}_ids") do
75
+ reload_connection()
76
+ if send(reflection.name).loaded? || reflection.options[:finder_sql]
77
+ send(reflection.name).map(&:id)
78
+ else
79
+ send(reflection.name).all(:select => "#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map(&:id)
80
+ end
81
+ end
82
+ end
83
+
84
+ def collection_accessor_methods(reflection, association_proxy_class, writer = true)
85
+ collection_reader_method(reflection, association_proxy_class)
86
+
87
+ if writer
88
+ define_method("#{reflection.name}=") do |new_value|
89
+ reload_connection()
90
+ # Loads proxy class instance (defined in collection_reader_method) if not already loaded
91
+ association = send(reflection.name)
92
+ association.replace(new_value)
93
+ association
94
+ end
95
+
96
+ define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
97
+ reload_connection()
98
+ ids = (new_value || []).reject { |nid| nid.blank? }.map(&:to_i)
99
+ send("#{reflection.name}=", reflection.klass.find(ids).index_by(&:id).values_at(*ids))
100
+ end
101
+ end
102
+ end
103
+
104
+ def association_constructor_method(constructor, reflection, association_proxy_class)
105
+ define_method("#{constructor}_#{reflection.name}") do |*params|
106
+ reload_connection()
107
+ attributees = params.first unless params.empty?
108
+ replace_existing = params[1].nil? ? true : params[1]
109
+ association = association_instance_get(reflection.name)
110
+
111
+ unless association
112
+ association = association_proxy_class.new(self, reflection)
113
+ association_instance_set(reflection.name, association)
114
+ end
115
+
116
+ if association_proxy_class == ActiveRecord::Associations::HasOneAssociation
117
+ ret_val = association.send(constructor, attributees, replace_existing)
118
+ else
119
+ ret_val = association.send(constructor, attributees)
120
+ end
121
+
122
+ if should_set_current_shard?
123
+ ret_val.current_shard = self.current_shard
124
+ end
125
+
126
+ return ret_val
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ ActiveRecord::Base.extend(Octopus::Rails2::Association)
@@ -0,0 +1,45 @@
1
+ module Octopus
2
+ module Rails2
3
+ module Persistence
4
+ def self.included(base)
5
+ base.instance_eval do
6
+ alias_method_chain :destroy, :octopus
7
+ alias_method_chain :delete, :octopus
8
+ alias_method_chain :reload, :octopus
9
+ end
10
+ end
11
+
12
+ def delete_with_octopus()
13
+ if should_set_current_shard?
14
+ self.using(self.current_shard) do
15
+ delete_without_octopus()
16
+ end
17
+ else
18
+ delete_without_octopus()
19
+ end
20
+ end
21
+
22
+ def destroy_with_octopus()
23
+ if should_set_current_shard?
24
+ self.using(self.current_shard) do
25
+ destroy_without_octopus()
26
+ end
27
+ else
28
+ destroy_without_octopus()
29
+ end
30
+ end
31
+
32
+ def reload_with_octopus(options = nil)
33
+ if should_set_current_shard?
34
+ self.using(self.current_shard) do
35
+ reload_without_octopus(options)
36
+ end
37
+ else
38
+ reload_without_octopus(options)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ ActiveRecord::Base.send(:include, Octopus::Rails2::Persistence)
@@ -0,0 +1,112 @@
1
+ module Octopus
2
+ module Rails3
3
+ module Association
4
+ def collection_reader_method(reflection, association_proxy_class)
5
+ define_method(reflection.name) do |*params|
6
+ force_reload = params.first unless params.empty?
7
+ reload_connection()
8
+
9
+ association = association_instance_get(reflection.name)
10
+
11
+ unless association
12
+ association = association_proxy_class.new(self, reflection)
13
+ association_instance_set(reflection.name, association)
14
+ end
15
+
16
+ reflection.klass.uncached { association.reload } if force_reload
17
+
18
+ association
19
+ end
20
+
21
+ define_method("#{reflection.name.to_s.singularize}_ids") do
22
+ reload_connection()
23
+ if send(reflection.name).loaded? || reflection.options[:finder_sql]
24
+ send(reflection.name).map(&:id)
25
+ else
26
+ if reflection.through_reflection && reflection.source_reflection.belongs_to?
27
+ through = reflection.through_reflection
28
+ primary_key = reflection.source_reflection.primary_key_name
29
+ send(through.name).select("DISTINCT #{through.quoted_table_name}.#{primary_key}").map!(&:"#{primary_key}")
30
+ else
31
+ send(reflection.name).select("#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").except(:includes).map!(&:id)
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ def association_constructor_method(constructor, reflection, association_proxy_class)
38
+ define_method("#{constructor}_#{reflection.name}") do |*params|
39
+ reload_connection()
40
+ attributees = params.first unless params.empty?
41
+ replace_existing = params[1].nil? ? true : params[1]
42
+ association = association_instance_get(reflection.name)
43
+
44
+ unless association
45
+ association = association_proxy_class.new(self, reflection)
46
+ association_instance_set(reflection.name, association)
47
+ end
48
+
49
+ if association_proxy_class == ActiveRecord::Associations::HasOneAssociation
50
+ return_val = association.send(constructor, attributees, replace_existing)
51
+ else
52
+ return_val = association.send(constructor, attributees)
53
+ end
54
+
55
+ if should_set_current_shard?
56
+ return_val.current_shard = self.current_shard
57
+ end
58
+
59
+ return_val
60
+ end
61
+ end
62
+
63
+ def association_accessor_methods(reflection, association_proxy_class)
64
+ define_method(reflection.name) do |*params|
65
+ reload_connection()
66
+ force_reload = params.first unless params.empty?
67
+ association = association_instance_get(reflection.name)
68
+
69
+ if association.nil? || force_reload
70
+ association = association_proxy_class.new(self, reflection)
71
+ retval = force_reload ? reflection.klass.uncached { association.reload } : association.reload
72
+ if retval.nil? and association_proxy_class == ActiveRecord::Associations::BelongsToAssociation
73
+ association_instance_set(reflection.name, nil)
74
+ return nil
75
+ end
76
+ association_instance_set(reflection.name, association)
77
+ end
78
+
79
+ association.target.nil? ? nil : association
80
+ end
81
+
82
+ define_method("loaded_#{reflection.name}?") do
83
+ reload_connection()
84
+ association = association_instance_get(reflection.name)
85
+ association && association.loaded?
86
+ end
87
+
88
+ define_method("#{reflection.name}=") do |new_value|
89
+ reload_connection()
90
+ association = association_instance_get(reflection.name)
91
+
92
+ if association.nil? || association.target != new_value
93
+ association = association_proxy_class.new(self, reflection)
94
+ end
95
+
96
+ association.replace(new_value)
97
+ association_instance_set(reflection.name, new_value.nil? ? nil : association)
98
+ end
99
+
100
+ define_method("set_#{reflection.name}_target") do |target|
101
+ return if target.nil? and association_proxy_class == ActiveRecord::Associations::BelongsToAssociation
102
+ reload_connection()
103
+ association = association_proxy_class.new(self, reflection)
104
+ association.target = target
105
+ association_instance_set(reflection.name, association)
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ ActiveRecord::Base.extend(Octopus::Rails3::Association)
@@ -0,0 +1,41 @@
1
+ module Octopus
2
+ module Rails3
3
+ module Persistence
4
+ def reload_connection()
5
+ set_connection() if should_set_current_shard?
6
+ end
7
+
8
+ def update_attribute(name, value)
9
+ reload_connection()
10
+ super(name, value)
11
+ end
12
+
13
+ def update_attributes(attributes)
14
+ reload_connection()
15
+ super(attributes)
16
+ end
17
+
18
+ def update_attributes!(attributes)
19
+ reload_connection()
20
+ super(attributes)
21
+ end
22
+
23
+ def reload
24
+ reload_connection()
25
+ super
26
+ end
27
+
28
+ def delete
29
+ reload_connection()
30
+ super
31
+ end
32
+
33
+ def destroy
34
+ reload_connection()
35
+ super
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ ActiveRecord::Base.send(:include, Octopus::Rails3::Persistence)
@@ -16,6 +16,11 @@ octopus:
16
16
  database: octopus_shard1
17
17
  encoding: unicode
18
18
 
19
+ #TODO - FIX THIS
20
+ sqlite_shard:
21
+ adapter: sqlite3
22
+ database: /Users/tchandy/Projetos/octopus/spec/database.sqlite3
23
+
19
24
  history_shards:
20
25
  aug2009:
21
26
  adapter: mysql
@@ -26,12 +26,16 @@ describe Octopus::Controller do
26
26
  end
27
27
 
28
28
  UsersControllers.action_methods.include?("create").should be_true
29
- a = UsersControllers.new
30
- a.stub!(:request).and_return(mock({:fullpath => "", :filtered_parameters => {}, :formats => ["xml"], :method => "GET"}))
31
- a.instance_variable_set(:@_response, mock(:content_type => "xml", :body= => "", :status => 401))
32
- a.process(:create)
33
-
34
- User.using(:brazil).find_by_name("ActionController").should_not be_nil
35
- User.using(:master).find_by_name("ActionController").should be_nil
29
+
30
+ if Octopus.rails3?
31
+ a = UsersControllers.new
32
+ a.stub!(:request).and_return(mock({:fullpath => "", :filtered_parameters => {}, :formats => ["xml"], :method => "GET"}))
33
+ a.instance_variable_set(:@_response, mock(:content_type => "xml", :body= => "", :status => 401))
34
+ a.process(:create)
35
+ User.using(:brazil).find_by_name("ActionController").should_not be_nil
36
+ User.using(:master).find_by_name("ActionController").should be_nil
37
+ else
38
+ pending()
39
+ end
36
40
  end
37
41
  end
@@ -32,18 +32,18 @@ describe Octopus::Migration do
32
32
 
33
33
  it "should run on multiples groups" do
34
34
  migrating_to_version 5 do
35
- User.using(:canada).where(:name => "MultipleGroup").all.size.should == 2
36
- User.using(:brazil).where(:name => "MultipleGroup").all.size.should == 2
37
- User.using(:russia).where(:name => "MultipleGroup").all.size.should == 2
35
+ User.using(:canada).find(:all, {:conditions => {:name => "MultipleGroup"}}).size.should == 2
36
+ User.using(:brazil).find(:all, {:conditions => {:name => "MultipleGroup"}}).size.should == 2
37
+ User.using(:russia).find(:all, {:conditions => {:name => "MultipleGroup"}}).size.should == 2
38
38
  end
39
39
  end
40
40
 
41
41
  it "should create users inside block" do
42
42
  migrating_to_version 12 do
43
- User.using(:brazil).where(:name => "UsingBlock1").all.size.should == 1
44
- User.using(:brazil).where(:name => "UsingBlock2").all.size.should == 1
45
- User.using(:canada).where(:name => "UsingCanada").all.size.should == 1
46
- User.using(:canada).where(:name => "UsingCanada2").all.size.should == 1
43
+ User.using(:brazil).find(:all, :conditions => {:name => "UsingBlock1"}).size.should == 1
44
+ User.using(:brazil).find(:all, :conditions => {:name => "UsingBlock2"}).size.should == 1
45
+ User.using(:canada).find(:all, :conditions => {:name => "UsingCanada"}).size.should == 1
46
+ User.using(:canada).find(:all, :conditions => {:name => "UsingCanada2"}).size.should == 1
47
47
  end
48
48
  end
49
49
 
@@ -69,8 +69,8 @@ describe Octopus::Migration do
69
69
  it "should run writes on master when you use replication" do
70
70
  using_enviroment :production_replicated do
71
71
  migrating_to_version 10 do
72
- Cat.using(:master).where(:name => "Replication").first.should_not be_nil
73
- Cat.where(:name => "Replication").first.should be_nil
72
+ Cat.using(:master).find_by_name("Replication").should_not be_nil
73
+ Cat.find_by_name("Replication").should be_nil
74
74
  end
75
75
  end
76
76
  end
@@ -37,6 +37,11 @@ describe Octopus::Model do
37
37
  User.count.should == 1
38
38
  end
39
39
 
40
+ it "should work when you have a SQLite3 shard" do
41
+ u = User.using(:sqlite_shard).create!(:name => "Sqlite3")
42
+ User.using(:sqlite_shard).find_by_name("Sqlite3").should == u
43
+ end
44
+
40
45
  it "should clean #current_shard from proxy when using execute" do
41
46
  ActiveRecord::Base.using(:canada).connection().execute("select * from users limit 1;")
42
47
  ActiveRecord::Base.connection.current_shard.should == :master
@@ -55,7 +60,7 @@ describe Octopus::Model do
55
60
  User.first.should == @user
56
61
  end
57
62
 
58
- User.using(:brazil).where(:name => "Thiago").first.should == @user
63
+ User.using(:brazil).find_by_name("Thiago").should == @user
59
64
  end
60
65
 
61
66
  it "should clean the current_shard after executing the current query" do
@@ -82,6 +87,7 @@ describe Octopus::Model do
82
87
 
83
88
  it "should store the attribute when you find multiple instances" do
84
89
  5.times { User.using(:alone_shard).create!(:name => "Alone") }
90
+
85
91
  User.using(:alone_shard).all.each do |u|
86
92
  u.current_shard.should == :alone_shard
87
93
  end
@@ -98,7 +104,7 @@ describe Octopus::Model do
98
104
 
99
105
  it "should work for the reload method" do
100
106
  User.using(:alone_shard).create!(:name => "Alone")
101
- u = User.using(:alone_shard).where(:name => "Alone").first
107
+ u = User.using(:alone_shard).find_by_name("Alone")
102
108
  u.reload
103
109
  u.name.should == "Alone"
104
110
  end
@@ -132,13 +138,15 @@ describe Octopus::Model do
132
138
 
133
139
  describe "using a postgresql shard" do
134
140
  it "should update the Arel Engine" do
135
- User.using(:postgresql_shard).arel_engine.connection.adapter_name.should == "PostgreSQL"
136
- User.using(:alone_shard).arel_engine.connection.adapter_name.should == "MySQL"
141
+ if ActiveRecord::VERSION::STRING > '2.4.0'
142
+ User.using(:postgresql_shard).arel_engine.connection.adapter_name.should == "PostgreSQL"
143
+ User.using(:alone_shard).arel_engine.connection.adapter_name.should == "MySQL"
144
+ end
137
145
  end
138
146
 
139
147
  it "should works with writes and reads" do
140
148
  u = User.using(:postgresql_shard).create!(:name => "PostgreSQL User")
141
- User.using(:postgresql_shard).all.should == [u]
149
+ User.using(:postgresql_shard).find(:all).should == [u]
142
150
  User.using(:alone_shard).find(:all).should == []
143
151
  end
144
152
  end
@@ -178,14 +186,14 @@ describe Octopus::Model do
178
186
  u = User.using(:brazil).create!(:name => "User1")
179
187
  u2 = User.using(:brazil).create!(:name => "User2")
180
188
  u3 = User.using(:brazil).create!(:name => "User3")
181
- User.using(:brazil).where(:name => "User2").count.should == 1
189
+ User.using(:brazil).find(:all, :conditions => {:name => "User2"}).count.should == 1
182
190
  end
183
191
 
184
192
  it "update_attributes" do
185
193
  @user = User.using(:brazil).create!(:name => "User1")
186
194
  @user2 = User.using(:brazil).find(@user.id)
187
195
  @user2.update_attributes(:name => "Joaquim")
188
- User.using(:brazil).where(:name => "Joaquim").first.should_not be_nil
196
+ User.using(:brazil).find_by_name("Joaquim").should_not be_nil
189
197
  end
190
198
 
191
199
  it "using update_attributes inside a block" do
@@ -195,15 +203,15 @@ describe Octopus::Model do
195
203
  @user2.update_attributes(:name => "Joaquim")
196
204
  end
197
205
 
198
- User.where(:name => "Joaquim").first.should be_nil
199
- User.using(:brazil).where(:name => "Joaquim").first.should_not be_nil
206
+ User.find_by_name("Joaquim").should be_nil
207
+ User.using(:brazil).find_by_name("Joaquim").should_not be_nil
200
208
  end
201
209
 
202
210
  it "update_attribute" do
203
211
  @user = User.using(:brazil).create!(:name => "User1")
204
212
  @user2 = User.using(:brazil).find(@user.id)
205
213
  @user2.update_attributes(:name => "Joaquim")
206
- User.using(:brazil).where(:name => "Joaquim").first.should_not be_nil
214
+ User.using(:brazil).find_by_name("Joaquim").should_not be_nil
207
215
  end
208
216
 
209
217
  it "transaction" do
@@ -213,7 +221,7 @@ describe Octopus::Model do
213
221
  User.using(:master).count.should == 1
214
222
 
215
223
  User.using(:brazil).transaction do
216
- User.where(:name => "Thiago").first.should be_nil
224
+ User.find_by_name("Thiago").should be_nil
217
225
  User.create!(:name => "Brazil")
218
226
  end
219
227
 
@@ -5,7 +5,7 @@ describe Octopus::Proxy do
5
5
 
6
6
  describe "creating a new instance" do
7
7
  it "should initialize all shards and groups" do
8
- proxy.instance_variable_get(:@shards).keys.to_set.should == [:postgresql_shard, :alone_shard, :aug2011, :canada, :brazil, :aug2009, :russia, :aug2010, :master].to_set
8
+ proxy.instance_variable_get(:@shards).keys.to_set.should == [:postgresql_shard, :alone_shard, :aug2011, :canada, :brazil, :aug2009, :russia, :aug2010, :master, :sqlite_shard].to_set
9
9
  proxy.instance_variable_get(:@groups).should == {:country_shards=>[:canada, :brazil, :russia], :history_shards=>[:aug2009, :aug2010, :aug2011]}
10
10
  end
11
11
 
@@ -2,12 +2,14 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
3
  describe Octopus::ScopeProxy do
4
4
  it "should allow nested queries" do
5
- @user1 = User.using(:brazil).create!(:name => "Thiago P", :number => 3)
6
- @user2 = User.using(:brazil).create!(:name => "Thiago", :number => 1)
7
- @user3 = User.using(:brazil).create!(:name => "Thiago", :number => 2)
5
+ if Octopus.rails3?
6
+ @user1 = User.using(:brazil).create!(:name => "Thiago P", :number => 3)
7
+ @user2 = User.using(:brazil).create!(:name => "Thiago", :number => 1)
8
+ @user3 = User.using(:brazil).create!(:name => "Thiago", :number => 2)
8
9
 
9
- User.using(:brazil).where(:name => "Thiago").where(:number => 4).order(:number).all.should == []
10
- User.using(:brazil).where(:name => "Thiago").using(:canada).where(:number => 2).using(:brazil).order(:number).all.should == [@user3]
11
- User.using(:brazil).where(:name => "Thiago").using(:canada).where(:number => 4).using(:brazil).order(:number).all.should == []
10
+ User.using(:brazil).where(:name => "Thiago").where(:number => 4).order(:number).all.should == []
11
+ User.using(:brazil).where(:name => "Thiago").using(:canada).where(:number => 2).using(:brazil).order(:number).all.should == [@user3]
12
+ User.using(:brazil).where(:name => "Thiago").using(:canada).where(:number => 4).using(:brazil).order(:number).all.should == []
13
+ end
12
14
  end
13
15
  end
@@ -1,4 +1,12 @@
1
+ require "rubygems"
1
2
  MIGRATIONS_ROOT = File.expand_path(File.join(File.dirname(__FILE__), 'migrations'))
3
+
4
+ if ENV["VERSION"]
5
+ gem 'activerecord', ENV["VERSION"]
6
+ gem 'activesupport', ENV["VERSION"]
7
+ gem 'actionpack', ENV["VERSION"]
8
+ end
9
+
2
10
  require 'spec'
3
11
  require 'spec/autorun'
4
12
  require "spec/database_connection"
@@ -6,6 +14,7 @@ require "action_controller"
6
14
  require 'octopus'
7
15
  require "octopus_helper"
8
16
 
17
+
9
18
  Spec::Runner.configure do |config|
10
19
  config.before(:each) do
11
20
  Octopus.stub!(:directory).and_return(File.dirname(__FILE__))
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ar-octopus
3
3
  version: !ruby/object:Gem::Version
4
- hash: 57
4
+ hash: 55
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 19
10
- version: 0.0.19
9
+ - 20
10
+ version: 0.0.20
11
11
  platform: ruby
12
12
  authors:
13
13
  - Thiago Pradi
@@ -17,7 +17,7 @@ autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
19
 
20
- date: 2010-07-05 00:00:00 -03:00
20
+ date: 2010-07-07 00:00:00 -03:00
21
21
  default_executable:
22
22
  dependencies:
23
23
  - !ruby/object:Gem::Dependency
@@ -70,6 +70,7 @@ files:
70
70
  - doc/features.textile
71
71
  - doc/libraries.textile
72
72
  - doc/shards.yml
73
+ - init.rb
73
74
  - lib/octopus.rb
74
75
  - lib/octopus/association.rb
75
76
  - lib/octopus/association_collection.rb
@@ -77,8 +78,11 @@ files:
77
78
  - lib/octopus/has_and_belongs_to_many_association.rb
78
79
  - lib/octopus/migration.rb
79
80
  - lib/octopus/model.rb
80
- - lib/octopus/persistence.rb
81
81
  - lib/octopus/proxy.rb
82
+ - lib/octopus/rails2/association.rb
83
+ - lib/octopus/rails2/persistence.rb
84
+ - lib/octopus/rails3/association.rb
85
+ - lib/octopus/rails3/persistence.rb
82
86
  - lib/octopus/scope_proxy.rb
83
87
  - rails/init.rb
84
88
  - spec/config/shards.yml
@@ -1,37 +0,0 @@
1
- module Octopus::Persistence
2
- def reload_connection()
3
- set_connection() if should_set_current_shard?
4
- end
5
-
6
- def update_attribute(name, value)
7
- reload_connection()
8
- super(name, value)
9
- end
10
-
11
- def update_attributes(attributes)
12
- reload_connection()
13
- super(attributes)
14
- end
15
-
16
- def update_attributes!(attributes)
17
- reload_connection()
18
- super(attributes)
19
- end
20
-
21
- def reload
22
- reload_connection()
23
- super
24
- end
25
-
26
- def delete
27
- reload_connection()
28
- super
29
- end
30
-
31
- def destroy
32
- reload_connection()
33
- super
34
- end
35
- end
36
-
37
- ActiveRecord::Base.send(:include, Octopus::Persistence)