activerecord-model-spaces 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|