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 +1 -0
- data/README.mkdn +2 -2
- data/Rakefile +2 -2
- data/ar-octopus.gemspec +7 -3
- data/init.rb +1 -0
- data/lib/octopus.rb +20 -6
- data/lib/octopus/association.rb +1 -106
- data/lib/octopus/association_collection.rb +9 -34
- data/lib/octopus/has_and_belongs_to_many_association.rb +15 -37
- data/lib/octopus/migration.rb +19 -2
- data/lib/octopus/model.rb +11 -1
- data/lib/octopus/rails2/association.rb +133 -0
- data/lib/octopus/rails2/persistence.rb +45 -0
- data/lib/octopus/rails3/association.rb +112 -0
- data/lib/octopus/rails3/persistence.rb +41 -0
- data/spec/config/shards.yml +5 -0
- data/spec/octopus/controller_spec.rb +11 -7
- data/spec/octopus/migration_spec.rb +9 -9
- data/spec/octopus/model_spec.rb +19 -11
- data/spec/octopus/proxy_spec.rb +1 -1
- data/spec/octopus/scope_proxy_spec.rb +8 -6
- data/spec/spec_helper.rb +9 -0
- metadata +9 -5
- data/lib/octopus/persistence.rb +0 -37
data/.gitignore
CHANGED
data/README.mkdn
CHANGED
@@ -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,
|
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.
|
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|
|
data/ar-octopus.gemspec
CHANGED
@@ -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.
|
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-
|
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')
|
data/lib/octopus.rb
CHANGED
@@ -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
|
-
|
41
|
-
require "octopus/migration"
|
44
|
+
|
42
45
|
require "octopus/model"
|
43
|
-
require "octopus/
|
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/
|
61
|
+
require "octopus/controller"
|
62
|
+
|
data/lib/octopus/association.rb
CHANGED
@@ -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
|
-
|
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(
|
7
|
-
if
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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)
|
data/lib/octopus/migration.rb
CHANGED
@@ -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
|
-
|
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)
|
data/lib/octopus/model.rb
CHANGED
@@ -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)
|
data/spec/config/shards.yml
CHANGED
@@ -26,12 +26,16 @@ describe Octopus::Controller do
|
|
26
26
|
end
|
27
27
|
|
28
28
|
UsersControllers.action_methods.include?("create").should be_true
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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).
|
36
|
-
User.using(:brazil).
|
37
|
-
User.using(:russia).
|
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).
|
44
|
-
User.using(:brazil).
|
45
|
-
User.using(:canada).
|
46
|
-
User.using(:canada).
|
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).
|
73
|
-
Cat.
|
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
|
data/spec/octopus/model_spec.rb
CHANGED
@@ -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).
|
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).
|
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
|
-
|
136
|
-
|
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).
|
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).
|
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.
|
199
|
-
User.using(:brazil).
|
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).
|
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.
|
224
|
+
User.find_by_name("Thiago").should be_nil
|
217
225
|
User.create!(:name => "Brazil")
|
218
226
|
end
|
219
227
|
|
data/spec/octopus/proxy_spec.rb
CHANGED
@@ -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
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
10
|
-
|
11
|
-
|
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
|
data/spec/spec_helper.rb
CHANGED
@@ -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:
|
4
|
+
hash: 55
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
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-
|
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
|
data/lib/octopus/persistence.rb
DELETED
@@ -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)
|