empty_eye 0.4.2 → 0.4.3
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/CHANGELOG.md +6 -1
- data/lib/empty_eye/active_record/base.rb +41 -85
- data/lib/empty_eye/associations/shard_association_scope.rb +1 -1
- data/lib/empty_eye/associations/shard_has_one_association.rb +1 -1
- data/lib/empty_eye/persistence.rb +4 -4
- data/lib/empty_eye/{primary_view_extension.rb → primary_shard.rb} +30 -43
- data/lib/empty_eye/relation.rb +2 -65
- data/lib/empty_eye/shard.rb +86 -111
- data/lib/empty_eye/shard_collection.rb +192 -0
- data/lib/empty_eye/shard_wrangler.rb +300 -0
- data/lib/empty_eye/version.rb +1 -1
- data/lib/empty_eye.rb +5 -4
- data/spec/spec_helper.rb +0 -6
- metadata +7 -7
- data/lib/empty_eye/view_extension.rb +0 -114
- data/lib/empty_eye/view_extension_collection.rb +0 -227
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,9 @@
|
|
1
|
-
# 0.4.
|
1
|
+
# 0.4.3 / 2012-03-11 / Grady Griffin
|
2
|
+
|
3
|
+
* major refactor to do less in active record base
|
4
|
+
* add view versioning to prevent creating views when not necessary
|
5
|
+
|
6
|
+
# 0.4.2 / 2012-03-11 / Grady Griffin
|
2
7
|
|
3
8
|
* reorganized some methods
|
4
9
|
* added logic to free up complex data structures once they are not needed
|
@@ -5,122 +5,78 @@ module ActiveRecord
|
|
5
5
|
|
6
6
|
#am i a mti class? easier than making a new class type ... i tried
|
7
7
|
def mti_class?
|
8
|
-
|
8
|
+
!!@shard_wrangler
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
#interface for building mti_class
|
12
12
|
#primary table is not necessary if the table named correctly (Bar => bars_core)
|
13
13
|
#OR if the class inherits a primary table
|
14
14
|
#simply wrap your greasy associations in this block
|
15
15
|
def mti_class(primary_table = nil)
|
16
|
-
self.primary_key = "id"
|
17
16
|
raise(EmptyEye::AlreadyExtended, "MTI class method already invoked") if mti_class?
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
self.primary_key = "id"
|
18
|
+
@shard_wrangler = EmptyEye::ShardWrangler.create(self, primary_table)
|
19
|
+
self.table_name = @shard_wrangler.compute_view_name
|
21
20
|
before_yield = reflect_on_multiple_associations(:has_one)
|
22
21
|
yield nil if block_given?
|
23
|
-
|
24
|
-
|
22
|
+
mti_ancestors = reflect_on_multiple_associations(:has_one) - before_yield
|
23
|
+
@shard_wrangler.wrangle_shards(mti_ancestors)
|
25
24
|
true
|
26
25
|
end
|
27
|
-
|
28
|
-
#
|
29
|
-
#
|
30
|
-
def
|
31
|
-
|
26
|
+
|
27
|
+
#we need this when we add new associaton types to extend with
|
28
|
+
#we could use the baked in version for now
|
29
|
+
def reflect_on_multiple_associations(*assoc_types)
|
30
|
+
assoc_types.collect do |assoc_type|
|
31
|
+
reflect_on_all_associations(assoc_type)
|
32
|
+
end.flatten.uniq
|
32
33
|
end
|
33
34
|
|
34
|
-
#the class of primary shard
|
35
|
-
def mti_primary_shard
|
36
|
-
extended_with.primary.shard
|
37
|
-
end
|
38
|
-
|
39
35
|
#we dont need no freakin' type condition
|
40
36
|
#the view handles this
|
41
37
|
def finder_needs_type_condition?
|
42
38
|
!mti_class? and super
|
43
39
|
end
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
#we will make a mti class accordingly here
|
49
|
-
def extend_mti_class(mti_associations)
|
50
|
-
mti_associations.each do |assoc|
|
51
|
-
extended_with.association(assoc)
|
52
|
-
end
|
53
|
-
create_view
|
54
|
-
reset_column_information
|
55
|
-
inherit_mti_validations
|
56
|
-
end
|
57
|
-
|
58
|
-
#we need a name for the view
|
59
|
-
#need to have a way to set this
|
60
|
-
def compute_view_name
|
61
|
-
descends_from_active_record? ? compute_table_name : name.underscore.pluralize
|
62
|
-
end
|
63
|
-
|
64
|
-
#determine the primary table
|
65
|
-
#first determine if our view name exists; this will need to change one day
|
66
|
-
#if they didnt specify try using the core convention else the superclass
|
67
|
-
#if they specified use what they set
|
68
|
-
def set_mti_primary_table(primary_table_name)
|
69
|
-
@mti_primary_table = if ordinary_table_exists?
|
70
|
-
raise(EmptyEye::ViewNameError, "MTI view cannot be created because a table named '#{compute_view_name}' already exists")
|
71
|
-
elsif primary_table_name.nil?
|
72
|
-
descends_from_active_record? ? "#{compute_table_name}_core" : superclass.table_name
|
73
|
-
else
|
74
|
-
primary_table_name
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def mti_primary_table
|
79
|
-
@mti_primary_table
|
80
|
-
end
|
81
|
-
|
82
|
-
#we need this when we add new associaton types to extend with
|
83
|
-
#we could use the baked in version for now
|
84
|
-
def reflect_on_multiple_associations(*assoc_types)
|
85
|
-
assoc_types.collect do |assoc_type|
|
86
|
-
reflect_on_all_associations(assoc_type)
|
87
|
-
end.flatten.uniq
|
40
|
+
|
41
|
+
#remove the schema_version for mti_class views
|
42
|
+
def column_names
|
43
|
+
@column_names ||= columns.map { |column| column.name } - (mti_class? ? ["schema_version"] : [])
|
88
44
|
end
|
89
45
|
|
90
|
-
#
|
91
|
-
def
|
92
|
-
|
46
|
+
#the class of primary shard
|
47
|
+
def shard_wrangler
|
48
|
+
@shard_wrangler
|
93
49
|
end
|
94
50
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
51
|
+
def descends_from_active_record?
|
52
|
+
if superclass.abstract_class?
|
53
|
+
superclass.descends_from_active_record?
|
54
|
+
elsif mti_class?
|
55
|
+
superclass == Base
|
56
|
+
else
|
57
|
+
superclass == Base || !columns_hash.include?(inheritance_column)
|
58
|
+
end
|
100
59
|
end
|
101
60
|
|
102
|
-
|
103
|
-
def superclass_extensions
|
104
|
-
superclass.extended_with.dup.descend(self) unless descends_from_active_record?
|
105
|
-
end
|
61
|
+
private
|
106
62
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
63
|
+
end
|
64
|
+
|
65
|
+
def valid?(context = nil)
|
66
|
+
context ||= (new_record? ? :create : :update)
|
67
|
+
output = super(context)
|
68
|
+
return errors.empty? && output unless mti_class?
|
69
|
+
shard_wrangler.valid?(context) && errors.empty? && output
|
114
70
|
end
|
115
71
|
|
116
72
|
private
|
117
73
|
|
118
74
|
#a pseudo association method mapping us back to instances primary shard
|
119
|
-
def
|
120
|
-
@
|
121
|
-
self.class.
|
75
|
+
def shard_wrangler
|
76
|
+
@shard_wrangler ||= if new_record?
|
77
|
+
self.class.shard_wrangler.new(:mti_instance => self)
|
122
78
|
else
|
123
|
-
rtn = self.class.
|
79
|
+
rtn = self.class.shard_wrangler.find_by_id(id)
|
124
80
|
rtn.mti_instance = self
|
125
81
|
rtn
|
126
82
|
end
|
@@ -45,7 +45,7 @@ module EmptyEye
|
|
45
45
|
scope = scope.where(table[key].eq(owner[foreign_key]))
|
46
46
|
|
47
47
|
if reflection.type
|
48
|
-
scope = scope.where(table[reflection.type].eq(owner.
|
48
|
+
scope = scope.where(table[reflection.type].eq(owner.master_class.base_class.name))
|
49
49
|
end
|
50
50
|
|
51
51
|
conditions.each do |condition|
|
@@ -22,7 +22,7 @@ module EmptyEye
|
|
22
22
|
attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
|
23
23
|
|
24
24
|
if reflection.options[:as]
|
25
|
-
attributes[reflection.type] = owner.
|
25
|
+
attributes[reflection.type] = owner.master_class.base_class.name
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
@@ -6,7 +6,7 @@ module EmptyEye
|
|
6
6
|
#else let the primary shard do the saving
|
7
7
|
def update(attribute_names = @attributes.keys)
|
8
8
|
return super unless mti_class?
|
9
|
-
|
9
|
+
shard_wrangler.cascade_save
|
10
10
|
1
|
11
11
|
end
|
12
12
|
|
@@ -15,7 +15,7 @@ module EmptyEye
|
|
15
15
|
#come back and cleanup
|
16
16
|
def create
|
17
17
|
return super unless mti_class?
|
18
|
-
|
18
|
+
shard_wrangler.cascade_save
|
19
19
|
ActiveRecord::IdentityMap.add(self) if ActiveRecord::IdentityMap.enabled?
|
20
20
|
@new_record = false
|
21
21
|
self.id
|
@@ -26,7 +26,7 @@ module EmptyEye
|
|
26
26
|
#come back and cleanup
|
27
27
|
def destroy
|
28
28
|
return super unless mti_class?
|
29
|
-
|
29
|
+
shard_wrangler.destroy
|
30
30
|
if ActiveRecord::IdentityMap.enabled? and persisted?
|
31
31
|
ActiveRecord::IdentityMap.remove(self)
|
32
32
|
end
|
@@ -39,7 +39,7 @@ module EmptyEye
|
|
39
39
|
#come back and cleanup
|
40
40
|
def delete
|
41
41
|
return super unless mti_class?
|
42
|
-
|
42
|
+
shard_wrangler.class.cascade_delete_all(:id => id)
|
43
43
|
if ActiveRecord::IdentityMap.enabled? and persisted?
|
44
44
|
ActiveRecord::IdentityMap.remove(self)
|
45
45
|
end
|
@@ -1,14 +1,14 @@
|
|
1
1
|
module EmptyEye
|
2
|
-
class
|
2
|
+
class PrimaryShard
|
3
3
|
|
4
|
-
#primary
|
4
|
+
#primary shard for master_class class
|
5
5
|
#manages associations for database updates
|
6
|
-
#has many of the same interfaces as view
|
6
|
+
#has many of the same interfaces as view shards
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
@table = table_name
|
10
|
-
@
|
11
|
-
|
8
|
+
def initialize(wrangler)
|
9
|
+
@table = wrangler.table_name
|
10
|
+
@master_class = wrangler.master_class
|
11
|
+
@klass = wrangler
|
12
12
|
end
|
13
13
|
|
14
14
|
def self.connection
|
@@ -17,12 +17,12 @@ module EmptyEye
|
|
17
17
|
|
18
18
|
#never include the type field as it shouldnt be needed and cant be updated anyway
|
19
19
|
def self.exclude_always
|
20
|
-
['type']
|
20
|
+
['type', 'mti_schema_version']
|
21
21
|
end
|
22
22
|
|
23
|
-
#class to which this
|
24
|
-
def
|
25
|
-
@
|
23
|
+
#class to which this shard belongs
|
24
|
+
def master_class
|
25
|
+
@master_class
|
26
26
|
end
|
27
27
|
|
28
28
|
#to let the outside word know it is primary
|
@@ -30,9 +30,9 @@ module EmptyEye
|
|
30
30
|
true
|
31
31
|
end
|
32
32
|
|
33
|
-
#class that will mimic the associations of the
|
34
|
-
def
|
35
|
-
@
|
33
|
+
#class that will mimic the associations of the master_class for updating db
|
34
|
+
def klass
|
35
|
+
@klass
|
36
36
|
end
|
37
37
|
|
38
38
|
# the tablename
|
@@ -50,7 +50,7 @@ module EmptyEye
|
|
50
50
|
@arel_table ||= Arel::Table.new(table)
|
51
51
|
end
|
52
52
|
|
53
|
-
#this may change but for now the key is the primary id of the
|
53
|
+
#this may change but for now the key is the primary id of the master_class and shard
|
54
54
|
def key
|
55
55
|
arel_table[:id]
|
56
56
|
end
|
@@ -60,19 +60,19 @@ module EmptyEye
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def sti_also?
|
63
|
-
!
|
63
|
+
!master_class.descends_from_active_record?
|
64
64
|
end
|
65
65
|
|
66
66
|
#arel column of type field
|
67
67
|
def type_column
|
68
68
|
if sti_also?
|
69
|
-
arel_table[
|
69
|
+
arel_table[master_class.inheritance_column.to_sym]
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
73
|
#value of the polymorphic column
|
74
74
|
def type_value
|
75
|
-
|
75
|
+
master_class.name if type_column
|
76
76
|
end
|
77
77
|
|
78
78
|
#always null for primary
|
@@ -82,7 +82,8 @@ module EmptyEye
|
|
82
82
|
|
83
83
|
#table columns
|
84
84
|
def table_columns
|
85
|
-
|
85
|
+
klass.column_names
|
86
|
+
#self.class.connection.columns(table).collect(&:name)
|
86
87
|
end
|
87
88
|
|
88
89
|
def exclude
|
@@ -94,22 +95,22 @@ module EmptyEye
|
|
94
95
|
table_columns - exclude
|
95
96
|
end
|
96
97
|
|
97
|
-
#create associations for shard class to mimic
|
98
|
-
def
|
98
|
+
#create associations for shard class to mimic master_class
|
99
|
+
def has_another(shard)
|
99
100
|
#this is myself; dont associate
|
100
|
-
return if
|
101
|
-
mimic =
|
102
|
-
return if
|
101
|
+
return if shard.primary
|
102
|
+
mimic = shard.association
|
103
|
+
return if klass.reflect_on_association(mimic.name)
|
103
104
|
options = mimic.options.dup
|
104
105
|
options.merge!(default_has_one_options)
|
105
|
-
options.merge!(:foreign_key =>
|
106
|
-
|
106
|
+
options.merge!(:foreign_key => shard.foreign_key)
|
107
|
+
klass.send(mimic.macro, mimic.name, options)
|
107
108
|
end
|
108
109
|
|
109
110
|
#delegate setters to appropriate associations
|
110
|
-
def delegate_to(col,
|
111
|
-
return if
|
112
|
-
|
111
|
+
def delegate_to(col, shard)
|
112
|
+
return if shard.primary
|
113
|
+
klass.send(:delegate, "#{col}=", {:to => shard.name})
|
113
114
|
end
|
114
115
|
|
115
116
|
private
|
@@ -118,20 +119,6 @@ module EmptyEye
|
|
118
119
|
def default_has_one_options
|
119
120
|
{:autosave => true, :validate => true, :dependent => :destroy}
|
120
121
|
end
|
121
|
-
|
122
|
-
#if possible shard inherits from the superclass
|
123
|
-
def shard_inherit_from
|
124
|
-
parent.base_class == parent ? ActiveRecord::Base : parent.send(:superclass)
|
125
|
-
end
|
126
|
-
|
127
|
-
#create a class to manage the parents associations
|
128
|
-
def create_shard
|
129
|
-
new_class = Class.new(shard_inherit_from)
|
130
|
-
new_class.send(:include, Shard)
|
131
|
-
new_class.table_name = table
|
132
|
-
new_class.mti_master_class = parent
|
133
|
-
@shard = EmptyEye.const_set("#{parent.to_s}Shard", new_class)
|
134
|
-
end
|
135
122
|
|
136
123
|
end
|
137
124
|
end
|
data/lib/empty_eye/relation.rb
CHANGED
@@ -9,77 +9,14 @@ module EmptyEye
|
|
9
9
|
|
10
10
|
def delete_all(conditions = nil)
|
11
11
|
return super unless mti_class?
|
12
|
-
|
13
|
-
affected = 0
|
14
|
-
#if something goes wrong forget it all
|
15
|
-
transaction do
|
16
|
-
if conditions
|
17
|
-
#batch up ids
|
18
|
-
ids = select("`#{table_name}`.`#{primary_key}`").where(conditions).collect(&:id)
|
19
|
-
#delete all the shards of the mti class matching ids
|
20
|
-
mti_batch_perform(ids) do |ext, batch|
|
21
|
-
if ext.polymorphic_type
|
22
|
-
ext.shard.delete_all(ext.foreign_key => batch, ext.polymorphic_type => ext.type_value)
|
23
|
-
else
|
24
|
-
ext.shard.delete_all(ext.foreign_key => batch)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
else
|
28
|
-
#way simpler if there are no conditions; kill everyone
|
29
|
-
extended_with.each do |ext|
|
30
|
-
affected = [affected, ext.shard.delete_all].max
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
affected
|
12
|
+
shard_wrangler.cascade_delete_all(conditions)
|
35
13
|
end
|
36
14
|
|
37
15
|
def update_all(updates, conditions = nil, options = {})
|
38
16
|
return super unless mti_class?
|
39
17
|
raise(EmptyEye::InvalidUpdate, "update values for a MTI class must be a hash") unless updates.is_a?(Hash)
|
40
|
-
|
41
|
-
stringified_updates = updates.stringify_keys
|
42
|
-
affected = 0
|
43
|
-
transaction do
|
44
|
-
if conditions
|
45
|
-
#batch up ids
|
46
|
-
ids = select(arel_table[primary_key.to_sym]).where(conditions).apply_finder_options(options.slice(:limit, :order)).collect(&:id)
|
47
|
-
#update all the shards of the mti class matching ids
|
48
|
-
affected = mti_batch_perform(ids) do |ext, batch|
|
49
|
-
#delegate map ingests the update hash and regurgitates a smaller hash of the values the shard can handle
|
50
|
-
cols = extended_with.delegate_map(ext.name, stringified_updates)
|
51
|
-
cols.empty? ? 0 : ext.shard.update_all(cols, ext.foreign_key => batch)
|
52
|
-
end
|
53
|
-
else
|
54
|
-
#way simpler if there are no conditions; change the world!
|
55
|
-
extended_with.each do |ext|
|
56
|
-
cols = extended_with.delegate_map(ext.name, stringified_updates)
|
57
|
-
affected = [(cols.empty? ? 0 : ext.shard.update_all(cols)), affected].max
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
affected
|
18
|
+
shard_wrangler.cascade_update_all(updates, conditions, options)
|
62
19
|
end
|
63
|
-
|
64
|
-
private
|
65
|
-
|
66
|
-
def mti_clear_identity_map
|
67
|
-
ActiveRecord::IdentityMap.repository[symbolized_base_class].clear if ActiveRecord::IdentityMap.enabled?
|
68
|
-
end
|
69
|
-
|
70
|
-
#lets do 10000 at a time
|
71
|
-
def mti_batch_perform(ids)
|
72
|
-
affected = 0
|
73
|
-
until ids.to_a.empty?
|
74
|
-
current_ids = ids.pop(10000)
|
75
|
-
extended_with.each do |ext|
|
76
|
-
rtn = yield(ext, current_ids)
|
77
|
-
affected = [affected,rtn].max
|
78
|
-
end
|
79
|
-
end
|
80
|
-
affected
|
81
|
-
end
|
82
|
-
|
83
20
|
end
|
84
21
|
end
|
85
22
|
end
|
data/lib/empty_eye/shard.rb
CHANGED
@@ -1,133 +1,108 @@
|
|
1
1
|
module EmptyEye
|
2
|
-
|
2
|
+
class Shard
|
3
3
|
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
|
8
|
-
def
|
9
|
-
|
4
|
+
#shard for master_class class
|
5
|
+
#tracks associations for database updates managed by primary shard
|
6
|
+
#has many of the same interfaces as primary view shard
|
7
|
+
|
8
|
+
def initialize(association)
|
9
|
+
@association = association
|
10
10
|
end
|
11
11
|
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
#we want to avoid the lookup
|
16
|
-
def mti_instance
|
17
|
-
@mti_instance || mti_master_class.find_by_id(id)
|
12
|
+
#exclude from view generation always
|
13
|
+
def self.exclude_always
|
14
|
+
['id','created_at','updated_at','deleted_at', 'type', 'mti_schema_version']
|
18
15
|
end
|
19
16
|
|
20
|
-
#
|
21
|
-
def
|
22
|
-
@
|
17
|
+
#association that this shard will build upon
|
18
|
+
def association
|
19
|
+
@association
|
23
20
|
end
|
24
21
|
|
25
|
-
#
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
save
|
34
|
-
#reset the id and then reload
|
35
|
-
mti_instance.id = id
|
36
|
-
mti_instance.reload
|
22
|
+
#the table columns that will be extended in sql
|
23
|
+
def columns
|
24
|
+
restrictions - exclude
|
25
|
+
end
|
26
|
+
|
27
|
+
#never the primary
|
28
|
+
def primary
|
29
|
+
false
|
37
30
|
end
|
38
31
|
|
39
|
-
#
|
40
|
-
def
|
41
|
-
|
32
|
+
#table of the shard
|
33
|
+
def table
|
34
|
+
association.table_name
|
42
35
|
end
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
def mti_safe_attributes
|
48
|
-
mti_instance.attributes.except(
|
49
|
-
*self.mti_master_class.extended_with.primary.exclude
|
50
|
-
)
|
51
|
-
end
|
52
|
-
|
53
|
-
#all the instance shards should exist but lets be certain
|
54
|
-
#using an autobuild would be more efficient here
|
55
|
-
#we shouldnt load associations we dont need to
|
56
|
-
def cascade_build_associations
|
57
|
-
#go through each extension making sure it is exists and is loaded
|
58
|
-
mti_instance.class.extended_with.each do |ext|
|
59
|
-
next if ext.primary
|
60
|
-
assoc = send(ext.name)
|
61
|
-
assoc ||= send("build_#{ext.name}")
|
62
|
-
send("#{ext.name}=", assoc)
|
63
|
-
end
|
36
|
+
|
37
|
+
#name of the association
|
38
|
+
def name
|
39
|
+
association.name
|
64
40
|
end
|
65
41
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
reflection = klass.new(macro, name, options, active_record)
|
73
|
-
|
74
|
-
self.reflections = self.reflections.merge(name => reflection)
|
75
|
-
reflection
|
42
|
+
#used to create view
|
43
|
+
def arel_table
|
44
|
+
@arel_table ||= begin
|
45
|
+
t= Arel::Table.new(table)
|
46
|
+
t.table_alias = alias_name if alias_name != table
|
47
|
+
t
|
76
48
|
end
|
49
|
+
end
|
77
50
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
sti_column.eq(mti_master_class.name)
|
83
|
-
end
|
84
|
-
|
85
|
-
#overriding find_by_id
|
86
|
-
#this is used to retrieve the shard instance for the master instance
|
87
|
-
#the type column is removed
|
88
|
-
def find_by_id(val)
|
89
|
-
query = columns_except_type
|
90
|
-
query = query.where(arel_table[:id].eq(val))
|
91
|
-
find_by_sql(query.to_sql).first
|
92
|
-
end
|
93
|
-
|
94
|
-
#the shard uses a special association builder
|
95
|
-
def has_one(name, options = {})
|
96
|
-
Associations::Builder::ShardHasOne.build(self, name, options)
|
97
|
-
end
|
51
|
+
#foreign key of the shard; used in view generation and database updates
|
52
|
+
def foreign_key
|
53
|
+
association.foreign_key
|
54
|
+
end
|
98
55
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
56
|
+
def klass
|
57
|
+
association.klass
|
58
|
+
end
|
59
|
+
|
60
|
+
#arel column of polymorphic type field
|
61
|
+
def type_column
|
62
|
+
arel_table[polymorphic_type.to_sym] if polymorphic_type
|
63
|
+
end
|
64
|
+
|
65
|
+
#value of the polymorphic column
|
66
|
+
def type_value
|
67
|
+
master_class.base_class.name if polymorphic_type
|
68
|
+
end
|
103
69
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
def reset_column_information
|
111
|
-
@columns_except_type = nil
|
112
|
-
super
|
113
|
-
end
|
114
|
-
|
115
|
-
private
|
116
|
-
|
117
|
-
#build the arel query once and memoize it
|
118
|
-
#this is essentially the select to remove type column
|
119
|
-
def columns_except_type
|
120
|
-
@columns_except_type ||= begin
|
121
|
-
query = arel_table
|
122
|
-
(column_names - [inheritance_column]).each do |c|
|
123
|
-
query = query.project(arel_table[c.to_sym])
|
124
|
-
end
|
125
|
-
query
|
126
|
-
end
|
127
|
-
@columns_except_type.dup
|
128
|
-
end
|
70
|
+
def polymorphic_type
|
71
|
+
return unless association.options[:as]
|
72
|
+
"#{association.options[:as]}_type"
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
129
76
|
|
77
|
+
#class to whom this shard belongs
|
78
|
+
def master_class
|
79
|
+
association.active_record
|
80
|
+
end
|
81
|
+
|
82
|
+
#uses association name to create alias to prevent non unique aliases
|
83
|
+
def alias_name
|
84
|
+
name.to_s.pluralize
|
85
|
+
end
|
86
|
+
|
87
|
+
#user declared exceptions ... exclude these columns from the master_class inheritance
|
88
|
+
def exceptions
|
89
|
+
association.options[:except].to_a.collect(&:to_s)
|
90
|
+
end
|
91
|
+
|
92
|
+
#user declared restrictions ... restrict master_class inheritance columns to these
|
93
|
+
def restrictions
|
94
|
+
only = association.options[:only].to_a.collect(&:to_s)
|
95
|
+
only.empty? ? table_columns : only
|
96
|
+
end
|
97
|
+
|
98
|
+
#we want to omit these columns
|
99
|
+
def exclude
|
100
|
+
[exceptions, self.class.exclude_always, foreign_key, polymorphic_type].flatten.uniq
|
101
|
+
end
|
130
102
|
|
103
|
+
#all the columns of the shards table
|
104
|
+
def table_columns
|
105
|
+
klass.column_names
|
131
106
|
end
|
132
107
|
end
|
133
108
|
end
|