activerecord-model-spaces 0.2.0
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/.document +5 -0
- data/.rspec +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +56 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +98 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/activerecord-model-spaces.gemspec +87 -0
- data/lib/active_record/model_spaces.rb +89 -0
- data/lib/active_record/model_spaces/context.rb +184 -0
- data/lib/active_record/model_spaces/model_space.rb +121 -0
- data/lib/active_record/model_spaces/persistor.rb +72 -0
- data/lib/active_record/model_spaces/registry.rb +195 -0
- data/lib/active_record/model_spaces/table_manager.rb +111 -0
- data/lib/active_record/model_spaces/table_names.rb +51 -0
- data/lib/active_record/model_spaces/util.rb +55 -0
- data/spec/active_record/model_spaces/context_spec.rb +479 -0
- data/spec/active_record/model_spaces/model_space_spec.rb +292 -0
- data/spec/active_record/model_spaces/persistor_spec.rb +101 -0
- data/spec/active_record/model_spaces/registry_spec.rb +522 -0
- data/spec/active_record/model_spaces/table_manager_spec.rb +186 -0
- data/spec/active_record/model_spaces/table_names_spec.rb +90 -0
- data/spec/active_record/model_spaces/util_spec.rb +106 -0
- data/spec/active_record/model_spaces_spec.rb +116 -0
- data/spec/spec_helper.rb +18 -0
- metadata +205 -0
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'active_record/model_spaces/table_names'
|
2
|
+
require 'active_record/model_spaces/table_manager'
|
3
|
+
require 'active_record/model_spaces/util'
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module ModelSpaces
|
7
|
+
|
8
|
+
# holds the current and working tables for a ModelSpace
|
9
|
+
class Context
|
10
|
+
include Util
|
11
|
+
|
12
|
+
attr_reader :model_space
|
13
|
+
attr_reader :model_space_key
|
14
|
+
attr_reader :persistor
|
15
|
+
attr_reader :current_model_versions
|
16
|
+
attr_reader :working_model_versions
|
17
|
+
|
18
|
+
def initialize(model_space, model_space_key, persistor)
|
19
|
+
@model_space = model_space
|
20
|
+
@model_space_key = model_space_key.to_sym
|
21
|
+
@persistor = persistor
|
22
|
+
read_versions
|
23
|
+
create_tables
|
24
|
+
end
|
25
|
+
|
26
|
+
def read_versions
|
27
|
+
@current_model_versions = persistor.read_model_space_model_versions(model_space.name, model_space_key)
|
28
|
+
@working_model_versions = {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_tables
|
32
|
+
model_space.registered_model_keys.map do |model_name|
|
33
|
+
m = Util.model_from_name(model_name)
|
34
|
+
tm = TableManager.new(m)
|
35
|
+
tm.create_table(base_table_name(m), current_table_name(m))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.drop_tables(model_space, model_space_key)
|
40
|
+
model_space.registered_model_keys.map do |model_name|
|
41
|
+
m = Util.model_from_name(model_name)
|
42
|
+
tm = TableManager.new(m)
|
43
|
+
|
44
|
+
all_table_names = (0..model_space.history_versions(m)).map do |v|
|
45
|
+
TableNames.table_name(model_space.name, model_space_key, model_space.base_table_name(m), model_space.history_versions(m), v)
|
46
|
+
end
|
47
|
+
|
48
|
+
all_table_names.each do |table_name|
|
49
|
+
tm.drop_table(table_name)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# implements the Model.table_name method
|
55
|
+
def table_name(model)
|
56
|
+
version = get_working_model_version(model) || get_current_model_version(model)
|
57
|
+
table_name_from_model_version(model, version)
|
58
|
+
end
|
59
|
+
|
60
|
+
# base table name
|
61
|
+
def base_table_name(model)
|
62
|
+
model_space.base_table_name(model)
|
63
|
+
end
|
64
|
+
|
65
|
+
# table_name for version 0
|
66
|
+
def hoovered_table_name(model)
|
67
|
+
table_name_from_model_version(model, 0)
|
68
|
+
end
|
69
|
+
|
70
|
+
# current table_name, seen by everyone outside of this context
|
71
|
+
def current_table_name(model)
|
72
|
+
table_name_from_model_version(model, get_current_model_version(model))
|
73
|
+
end
|
74
|
+
|
75
|
+
# table_name which would be seen by this context in or after a new_version/updated_version. always returns a name
|
76
|
+
def next_table_name(model)
|
77
|
+
current_version = get_current_model_version(model)
|
78
|
+
next_version = TableNames.next_version(model_space.history_versions(model), current_version)
|
79
|
+
table_name_from_model_version(model, next_version)
|
80
|
+
end
|
81
|
+
|
82
|
+
# table_name of working table, seen by this context in or after new_version/updated_version. null if no new_version/updated_version has been issued/completed
|
83
|
+
def working_table_name(model)
|
84
|
+
table_name_from_model_version(model, get_working_model_version(model)) if get_working_model_version(model)
|
85
|
+
end
|
86
|
+
|
87
|
+
def new_version(model, copy_old_version=false, &block)
|
88
|
+
raise "new_version: a block must be supplied" if !block
|
89
|
+
|
90
|
+
if get_working_model_version(model)
|
91
|
+
block.call # nothing to do
|
92
|
+
else
|
93
|
+
current_version = get_current_model_version(model)
|
94
|
+
next_version = TableNames.next_version(model_space.history_versions(model), current_version)
|
95
|
+
|
96
|
+
tm = TableManager.new(model)
|
97
|
+
ok = false
|
98
|
+
begin
|
99
|
+
btn = base_table_name(model)
|
100
|
+
ctn = current_table_name(model)
|
101
|
+
ntn = next_table_name(model)
|
102
|
+
if next_version != current_version
|
103
|
+
tm.recreate_table(btn, ntn)
|
104
|
+
tm.copy_table(ctn, ntn) if copy_old_version
|
105
|
+
else # no history
|
106
|
+
tm.truncate_table(ntn) if !copy_old_version
|
107
|
+
end
|
108
|
+
set_working_model_version(model, next_version)
|
109
|
+
r = block.call
|
110
|
+
ok = true
|
111
|
+
r
|
112
|
+
ensure
|
113
|
+
delete_working_model_version(model) if !ok
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def updated_version(model, &block)
|
119
|
+
new_version(model, true, &block)
|
120
|
+
end
|
121
|
+
|
122
|
+
# copy all data to the base model-space tables and drop all history tables
|
123
|
+
def hoover
|
124
|
+
raise "can't hoover with active working versions: #{working_model_versions.keys.inspect}" if !working_model_versions.empty?
|
125
|
+
|
126
|
+
model_names = model_space.registered_model_keys
|
127
|
+
|
128
|
+
new_versions = Hash[ model_names.map do |model_name|
|
129
|
+
m = Util.model_from_name(model_name)
|
130
|
+
base_name = base_table_name(m)
|
131
|
+
current_name = current_table_name(m)
|
132
|
+
hoovered_name = hoovered_table_name(m)
|
133
|
+
|
134
|
+
tm = TableManager.new(m)
|
135
|
+
|
136
|
+
# copy to hoovered table
|
137
|
+
if current_name != hoovered_name
|
138
|
+
tm.recreate_table(base_name, hoovered_name)
|
139
|
+
tm.copy_table(current_name, hoovered_name)
|
140
|
+
end
|
141
|
+
|
142
|
+
# drop history tables
|
143
|
+
(1..model_space.history_versions(m)).map do |v|
|
144
|
+
htn = table_name_from_model_version(m, v)
|
145
|
+
tm.drop_table(htn)
|
146
|
+
end
|
147
|
+
|
148
|
+
[model_name, 0]
|
149
|
+
end ]
|
150
|
+
persistor.update_model_space_model_versions(new_versions)
|
151
|
+
|
152
|
+
read_versions
|
153
|
+
end
|
154
|
+
|
155
|
+
def commit
|
156
|
+
persistor.update_model_space_model_versions(model_space.name, model_space_key, current_model_versions.merge(working_model_versions))
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def table_name_from_model_version(model, version)
|
162
|
+
TableNames.table_name(model_space.name, model_space_key, model_space.base_table_name(model), model_space.history_versions(model), version)
|
163
|
+
end
|
164
|
+
|
165
|
+
def get_current_model_version(model)
|
166
|
+
raise "#{model}: not registered with ModelSpace: #{model_space.name}" if !model_space.is_registered?(model)
|
167
|
+
self.current_model_versions[model_space.registered_model_name(model)] || 0
|
168
|
+
end
|
169
|
+
|
170
|
+
def get_working_model_version(model)
|
171
|
+
raise "#{model}: not registered with ModelSpace: #{model_space.name}" if !model_space.is_registered?(model)
|
172
|
+
self.working_model_versions[model_space.registered_model_name(model)]
|
173
|
+
end
|
174
|
+
|
175
|
+
def set_working_model_version(model, version)
|
176
|
+
self.working_model_versions[model_space.registered_model_name(model)] = version
|
177
|
+
end
|
178
|
+
|
179
|
+
def delete_working_model_version(model)
|
180
|
+
self.working_model_versions.delete(model_space.registered_model_name(model))
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'active_support'
|
3
|
+
require 'active_record/model_spaces/context'
|
4
|
+
require 'active_record/model_spaces/persistor'
|
5
|
+
require 'active_record/model_spaces/table_names'
|
6
|
+
require 'active_record/model_spaces/util'
|
7
|
+
|
8
|
+
module ActiveRecord
|
9
|
+
module ModelSpaces
|
10
|
+
|
11
|
+
class << self
|
12
|
+
attr_accessor :connection
|
13
|
+
attr_accessor :table_name
|
14
|
+
end
|
15
|
+
|
16
|
+
# a ModelSpace has a set of models registered with it,
|
17
|
+
# from which a Context can be created
|
18
|
+
class ModelSpace
|
19
|
+
include Util
|
20
|
+
|
21
|
+
attr_reader :name
|
22
|
+
attr_reader :model_registrations
|
23
|
+
|
24
|
+
def initialize(name)
|
25
|
+
@name = name.to_sym
|
26
|
+
@model_registrations = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def register_model(model, opts=nil)
|
30
|
+
raise "#{model} is not an ActiveRecord model Class" if !is_active_record_model?(model)
|
31
|
+
opts ||= {}
|
32
|
+
ModelSpaces.check_model_registration_keys(opts.keys)
|
33
|
+
set_model_registration(model, opts.merge(:model=>model))
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def deregister_model(model)
|
38
|
+
delete_model_registration(model)
|
39
|
+
end
|
40
|
+
|
41
|
+
def history_versions(model)
|
42
|
+
get_model_registration(model)[:history_versions] || 0
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_base_table_name(model, table_name)
|
46
|
+
get_model_registration(model)[:base_table_name] = table_name
|
47
|
+
end
|
48
|
+
|
49
|
+
def registered_model(model)
|
50
|
+
get_model_registration(model)[:model]
|
51
|
+
end
|
52
|
+
|
53
|
+
def registered_model_name(model)
|
54
|
+
name_from_model(get_model_registration(model)[:model])
|
55
|
+
end
|
56
|
+
|
57
|
+
def base_table_name(model)
|
58
|
+
r = get_model_registration(model)
|
59
|
+
r[:base_table_name] || TableNames.base_table_name(r[:model])
|
60
|
+
end
|
61
|
+
|
62
|
+
def registered_model_keys
|
63
|
+
self.model_registrations.keys
|
64
|
+
end
|
65
|
+
|
66
|
+
def is_registered?(model)
|
67
|
+
!!unchecked_get_model_registration(model)
|
68
|
+
end
|
69
|
+
|
70
|
+
def create_context(model_space_key)
|
71
|
+
Context.new(self, model_space_key, ModelSpaces.create_persistor)
|
72
|
+
end
|
73
|
+
|
74
|
+
def kill_context(model_space_key)
|
75
|
+
Context.drop_tables(self, model_space_key)
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def unchecked_get_model_registration(model)
|
82
|
+
mc = all_model_superclasses(model).find do |klass|
|
83
|
+
model_registrations[name_from_model(klass)]
|
84
|
+
end
|
85
|
+
self.model_registrations[name_from_model(mc)] if mc
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_model_registration(model)
|
89
|
+
r = unchecked_get_model_registration(model)
|
90
|
+
raise "model: #{model.to_s} is not registered in ModelSpace: #{name}" if !r
|
91
|
+
r
|
92
|
+
end
|
93
|
+
|
94
|
+
def set_model_registration(model, registration)
|
95
|
+
self.model_registrations[name_from_model(model)] = registration
|
96
|
+
end
|
97
|
+
|
98
|
+
def delete_model_registration(model)
|
99
|
+
self.model_registrations.delete(name_from_model(model))
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
module_function
|
106
|
+
|
107
|
+
MODEL_REGISTRATION_KEYS = [:history_versions, :base_table_name].to_set
|
108
|
+
|
109
|
+
def check_model_registration_keys(keys)
|
110
|
+
unknown_keys = (keys.map(&:to_sym).to_set - MODEL_REGISTRATION_KEYS).to_a
|
111
|
+
raise "unknown keys: #{unknown_keys.inspect}" if !unknown_keys.empty?
|
112
|
+
end
|
113
|
+
|
114
|
+
# get a persistor given a connection... returns an instance of
|
115
|
+
# ActiveRecord::ModelSpaces::AdapterNamePersistor
|
116
|
+
def create_persistor
|
117
|
+
Persistor.new(ModelSpaces.connection || ActiveRecord::Base.connection, ModelSpaces.table_name)
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'active_record/model_spaces/util'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ModelSpaces
|
5
|
+
|
6
|
+
# manages ModelSpace persistence...
|
7
|
+
class Persistor
|
8
|
+
include Util
|
9
|
+
|
10
|
+
attr_reader :connection
|
11
|
+
attr_reader :table_name
|
12
|
+
|
13
|
+
def initialize(connection, table_name)
|
14
|
+
@connection = connection
|
15
|
+
@table_name = table_name || "model_spaces_tables"
|
16
|
+
create_model_spaces_table(@connection, @table_name)
|
17
|
+
end
|
18
|
+
|
19
|
+
# list all persisted prefixes for a given model space
|
20
|
+
def list_keys(model_space_name)
|
21
|
+
connection.select_rows("select model_space_key from #{table_name} where model_space_name='#{model_space_name}'").map{|r| r.first}
|
22
|
+
end
|
23
|
+
|
24
|
+
# returns a map of {ModelName => version} entries for a given model-space and model_space_key
|
25
|
+
def read_model_space_model_versions(model_space_name, model_space_key)
|
26
|
+
connection.select_all("select model_name, version from #{table_name} where model_space_name='#{model_space_name}' and model_space_key='#{model_space_key}'").reduce({}){|h,r| h[r["model_name"]] = r["version"].to_i ; h}
|
27
|
+
end
|
28
|
+
|
29
|
+
# update
|
30
|
+
def update_model_space_model_versions(model_space_name, model_space_key, new_model_versions)
|
31
|
+
ActiveRecord::Base.transaction do
|
32
|
+
old_model_versions = read_model_space_model_versions(model_space_name, model_space_key)
|
33
|
+
|
34
|
+
new_model_versions.map do |model_or_name, new_version|
|
35
|
+
model_name = name_from_model(model_or_name)
|
36
|
+
old_version = old_model_versions[model_name]
|
37
|
+
|
38
|
+
if old_version && new_version && old_version != new_version && new_version != 0
|
39
|
+
|
40
|
+
connection.execute("update #{table_name} set version=#{new_version} where model_space_name='#{model_space_name}' and model_space_key='#{model_space_key}' and model_name='#{model_name}'")
|
41
|
+
|
42
|
+
elsif !old_version && new_version && new_version != 0
|
43
|
+
|
44
|
+
connection.execute("insert into #{table_name} (model_space_name, model_space_key, model_name, version) values ('#{model_space_name}', '#{model_space_key}', '#{model_name}', #{new_version})")
|
45
|
+
|
46
|
+
elsif old_version && ( !new_version || new_version == 0 )
|
47
|
+
|
48
|
+
connection.execute("delete from #{table_name} where model_space_name='#{model_space_name}' and model_space_key='#{model_space_key}' and model_name='#{model_name}'")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# create the model_spaces table if it doesn't exist
|
56
|
+
def create_model_spaces_table(connection, tn)
|
57
|
+
if !connection.table_exists?(tn)
|
58
|
+
connection.instance_eval do
|
59
|
+
create_table(tn) do |t|
|
60
|
+
t.string :model_space_name, :null=>false
|
61
|
+
t.string :model_space_key, :null=>false
|
62
|
+
t.string :model_name, :null=>false
|
63
|
+
t.integer :version, :null=>false, :default=>0
|
64
|
+
end
|
65
|
+
add_index tn, [:model_space_name, :prefix, :model_name], :unique=>true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'active_record/model_spaces/model_space'
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
module ModelSpaces
|
6
|
+
|
7
|
+
class Registry
|
8
|
+
include Util
|
9
|
+
|
10
|
+
attr_reader :model_spaces
|
11
|
+
attr_reader :model_spaces_by_models
|
12
|
+
attr_reader :enforce_context
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
reset!
|
16
|
+
end
|
17
|
+
|
18
|
+
# drop all model_space and model registrations. will cause any with_model_space_context
|
19
|
+
# to most likely bork horribly
|
20
|
+
def reset!
|
21
|
+
@model_spaces = {}
|
22
|
+
@model_spaces_by_models = {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def register_model(model, model_space_name, opts={})
|
26
|
+
old_ms = unchecked_get_model_space_for_model(model)
|
27
|
+
old_ms.deregister_model(model) if old_ms
|
28
|
+
|
29
|
+
new_ms = register_model_space(model_space_name).register_model(model, opts)
|
30
|
+
register_model_space_for_model(model, new_ms)
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_base_table_name(model, table_name)
|
34
|
+
ms = unchecked_get_model_space_for_model(model)
|
35
|
+
raise "model #{model} is not (yet) registered to a ModelSpace. do in_model_space before set_table_name or use the :base_table_name option of in_model_space" if !ms
|
36
|
+
ms.set_base_table_name(model, table_name)
|
37
|
+
end
|
38
|
+
|
39
|
+
def base_table_name(model)
|
40
|
+
get_model_space_for_model(model).base_table_name(model)
|
41
|
+
end
|
42
|
+
|
43
|
+
def set_enforce_context(v)
|
44
|
+
@enforce_context = !!v
|
45
|
+
end
|
46
|
+
|
47
|
+
def table_name(model)
|
48
|
+
ctx = enforce_context ? get_context_for_model(model) : unchecked_get_context_for_model(model)
|
49
|
+
if ctx
|
50
|
+
ctx.table_name(model)
|
51
|
+
else
|
52
|
+
get_model_space_for_model(model).base_table_name(model)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def current_table_name(model)
|
57
|
+
get_context_for_model(model).current_table_name(model)
|
58
|
+
end
|
59
|
+
|
60
|
+
def working_table_name(model)
|
61
|
+
get_context_for_model(model).working_table_name(model)
|
62
|
+
end
|
63
|
+
|
64
|
+
# create a new version of the model
|
65
|
+
def new_version(model, &block)
|
66
|
+
get_context_for_model(model).new_version(model, &block)
|
67
|
+
end
|
68
|
+
|
69
|
+
# create an updated version of the model
|
70
|
+
def updated_version(model, &block)
|
71
|
+
get_context_for_model(model).updated_version(model, &block)
|
72
|
+
end
|
73
|
+
|
74
|
+
def hoover(model)
|
75
|
+
get_context_for_model(model).hoover
|
76
|
+
end
|
77
|
+
|
78
|
+
# execute a block with a ModelSpace context.
|
79
|
+
# only a single context can be active for a given ModelSpace on any Thread at
|
80
|
+
# any time, though different ModelSpaces may have active contexts concurrently
|
81
|
+
def with_context(model_space_name, model_space_key, &block)
|
82
|
+
|
83
|
+
ms = get_model_space(model_space_name)
|
84
|
+
raise "no such model space: #{model_space_name}" if !ms
|
85
|
+
|
86
|
+
current_ctx = merged_context[ms.name]
|
87
|
+
|
88
|
+
if current_ctx && current_ctx.model_space_key==model_space_key.to_sym
|
89
|
+
|
90
|
+
block.call # same context is already active
|
91
|
+
|
92
|
+
elsif current_ctx
|
93
|
+
|
94
|
+
raise "ModelSpace: #{model_space_name}: context with key #{current_ctx.model_space_key} already active"
|
95
|
+
|
96
|
+
else
|
97
|
+
|
98
|
+
old_merged_context = self.send(:merged_context)
|
99
|
+
ctx = ms.create_context(model_space_key)
|
100
|
+
context_stack << ctx
|
101
|
+
begin
|
102
|
+
self.merged_context = merge_context_stack
|
103
|
+
|
104
|
+
r = block.call
|
105
|
+
ctx.commit
|
106
|
+
r
|
107
|
+
ensure
|
108
|
+
context_stack.pop
|
109
|
+
self.merged_context = old_merged_context
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def kill_context(model_space_name, model_space_key)
|
116
|
+
get_model_space(model_space_name).kill_context(model_space_key)
|
117
|
+
end
|
118
|
+
|
119
|
+
# return the key of the active context for the given model_space, or nil
|
120
|
+
# if there is no active context
|
121
|
+
def active_key(model_space_name)
|
122
|
+
ms = get_model_space(model_space_name)
|
123
|
+
raise "no such model space: #{model_space_name}" if !ms
|
124
|
+
|
125
|
+
ctx = merged_context[ms.name]
|
126
|
+
|
127
|
+
ctx.model_space_key if ctx
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def register_model_space(model_space_name)
|
133
|
+
model_spaces[model_space_name.to_sym] ||= ModelSpace.new(model_space_name.to_sym)
|
134
|
+
end
|
135
|
+
|
136
|
+
def get_model_space(model_space_name)
|
137
|
+
model_spaces[model_space_name.to_sym]
|
138
|
+
end
|
139
|
+
|
140
|
+
def register_model_space_for_model(model, model_space)
|
141
|
+
model_spaces_by_models[name_from_model(model)] = model_space
|
142
|
+
end
|
143
|
+
|
144
|
+
def unchecked_get_model_space_for_model(model)
|
145
|
+
mc = all_model_superclasses(model).find do |klass|
|
146
|
+
model_spaces_by_models[name_from_model(klass)]
|
147
|
+
end
|
148
|
+
model_spaces_by_models[name_from_model(mc)] if mc
|
149
|
+
end
|
150
|
+
|
151
|
+
def get_model_space_for_model(model)
|
152
|
+
ms = unchecked_get_model_space_for_model(model)
|
153
|
+
raise "model: #{model} is not registered to any ModelSpace" if !ms
|
154
|
+
ms
|
155
|
+
end
|
156
|
+
|
157
|
+
CONTEXT_STACK_KEY = "ActiveRecord::ModelSpaces.context_stack"
|
158
|
+
|
159
|
+
def context_stack
|
160
|
+
Thread.current[CONTEXT_STACK_KEY] ||= []
|
161
|
+
end
|
162
|
+
|
163
|
+
MERGED_CONTEXT_KEY = "ActiveRecord::ModelSpaces.merged_context"
|
164
|
+
|
165
|
+
def merged_context
|
166
|
+
Thread.current[MERGED_CONTEXT_KEY] || {}
|
167
|
+
end
|
168
|
+
|
169
|
+
def merged_context=(mc)
|
170
|
+
Thread.current[MERGED_CONTEXT_KEY] = mc
|
171
|
+
end
|
172
|
+
|
173
|
+
# merge all entries in the context stack into a map
|
174
|
+
def merge_context_stack
|
175
|
+
context_stack.reduce({}) do |m, ctx|
|
176
|
+
raise "ModelSpace: #{ctx.model_space.name}: already has an active context" if m[ctx.model_space.name]
|
177
|
+
m[ctx.model_space.name] = ctx
|
178
|
+
m
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def unchecked_get_context_for_model(model)
|
183
|
+
ms = unchecked_get_model_space_for_model(model)
|
184
|
+
merged_context[ms.name] if ms
|
185
|
+
end
|
186
|
+
|
187
|
+
def get_context_for_model(model)
|
188
|
+
ms = get_model_space_for_model(model)
|
189
|
+
ctx = merged_context[ms.name]
|
190
|
+
raise "ModelSpace: '#{ms.name}' has no current context" if !ctx
|
191
|
+
ctx
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|