Class: Para::ComponentsConfiguration
- Inherits:
-
Object
- Object
- Para::ComponentsConfiguration
show all
- Defined in:
- lib/para/components_configuration.rb
Defined Under Namespace
Classes: Component, ComponentTooDeepError, Section, UndefinedComponentTypeError
Constant Summary
collapse
- RELOADABLE_ERRORS =
Errors that signal the cached component state went stale, usually after a schema migration, a database reset or when a component class was unloaded by the code reloader. When one of these is raised while resolving a component, we reset the caches (and optionally reload the tree) and retry instead of letting it bubble up and break rails commands or the server.
[
ActiveRecord::RecordNotFound,
ActiveRecord::StatementInvalid,
ActiveRecord::SubclassNotFound,
ActiveModel::MissingAttributeError,
NameError
].freeze
Instance Attribute Summary collapse
Instance Method Summary
collapse
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, &block) ⇒ Object
51
52
53
54
55
56
57
|
# File 'lib/para/components_configuration.rb', line 51
def method_missing(method, *args, &block)
if (component = resolve_component(method))
component.tap(&ActiveDecorator::Decorator.instance.method(:decorate))
else
super
end
end
|
Instance Attribute Details
#components_config_path ⇒ Object
Path to the app’s ‘config/components.rb`, set by the engine. Used to rebuild the components tree when auto-reloading after a stale lookup.
23
24
25
|
# File 'lib/para/components_configuration.rb', line 23
def components_config_path
@components_config_path
end
|
Instance Method Details
#component_configuration_for(identifier) ⇒ Object
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
# File 'lib/para/components_configuration.rb', line 143
def component_configuration_for(identifier)
sections.each do |section|
section.components.each do |component|
return component if component.identifier.to_s == identifier.to_s
component.child_components.each do |child_component|
return child_component if child_component.identifier.to_s == identifier.to_s
end
end
end
nil
end
|
#component_for(identifier) ⇒ Object
131
132
133
134
135
136
137
138
139
140
141
|
# File 'lib/para/components_configuration.rb', line 131
def component_for(identifier)
if (component = components_cache[identifier])
component
else
components_cache[identifier] = if (component_id = components_ids_hash[identifier])
Para::Component::Base.find(component_id)
else
Para::Component::Base.find_by(identifier: identifier)
end
end
end
|
#components_ids_hash ⇒ Object
164
165
166
|
# File 'lib/para/components_configuration.rb', line 164
def components_ids_hash
@components_ids_hash ||= {}.with_indifferent_access
end
|
#draw(&block) ⇒ Object
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
# File 'lib/para/components_configuration.rb', line 25
def draw(&block)
return unless components_installed?
Para::LogConfig.with_log_level(:fatal) do
log_level = Rails.logger.level
Rails.logger.level = :fatal
@sections = []
reset!
eager_load_components!
instance_eval(&block)
build
end
end
|
#reload! ⇒ Object
Re-runs the app’s ‘config/components.rb` to rebuild the whole components tree from scratch. Returns true on success, false if the file is missing or the reload itself fails (e.g. schema still mid-migration).
81
82
83
84
85
86
87
88
89
|
# File 'lib/para/components_configuration.rb', line 81
def reload!
path = components_config_path
return false unless path && File.exist?(path.to_s)
load(path.to_s)
true
rescue *RELOADABLE_ERRORS
false
end
|
#reset! ⇒ Object
Clears the memoized identifier -> database id maps and the per-request resolved instance caches, so the next lookup rebuilds against fresh data. Keeps the in-memory section/component definitions (pure Ruby, schema independent) untouched.
70
71
72
73
74
75
|
# File 'lib/para/components_configuration.rb', line 70
def reset!
@sections_ids_hash = nil
@components_ids_hash = nil
RequestStore.store[:components_cache] = nil
RequestStore.store[:sections_cache] = nil
end
|
#resolve_component(identifier, reload: true, retried: false) ⇒ Object
Resolves a component by identifier while healing stale caches. It covers both failure modes seen after a schema migration or a code reload:
* `component_for` raises a RELOADABLE_ERROR (stale id, missing column,
unloaded STI subclass) ;
* `component_for` returns nil because the ids hash is empty/stale, which
would otherwise surface as `undefined method 'xxx'` from method_missing.
On the first failure it resets the caches and – in auto-reload mode – rebuilds the tree from config/components.rb, then retries once. If the component still can’t be found it returns nil, letting method_missing raise the usual NoMethodError for genuinely unknown components.
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
# File 'lib/para/components_configuration.rb', line 116
def resolve_component(identifier, reload: true, retried: false)
component = component_for(identifier)
return component if component
return nil if retried || !reload
heal_components_cache!
resolve_component(identifier, reload: reload, retried: true)
rescue *RELOADABLE_ERRORS => error
return nil if retried || !reload
log_components_failure(error)
heal_components_cache!
resolve_component(identifier, reload: reload, retried: true)
end
|
#respond_to_missing?(method, include_private = false) ⇒ Boolean
59
60
61
62
63
|
# File 'lib/para/components_configuration.rb', line 59
def respond_to_missing?(method, include_private = false)
!resolve_component(method, reload: false).nil? || super
end
|
#section(*args, &block) ⇒ Object
43
44
45
|
# File 'lib/para/components_configuration.rb', line 43
def section(*args, &block)
sections << Section.new(*args, &block)
end
|
#section_for(identifier) ⇒ Object
91
92
93
94
95
96
97
98
99
100
101
|
# File 'lib/para/components_configuration.rb', line 91
def section_for(identifier)
if (section = sections_cache[identifier])
section
else
sections_cache[identifier] = if (section_id = sections_ids_hash[identifier])
Para::ComponentSection.find(section_id)
else
Para::ComponentSection.find_by(identifier: identifier)
end
end
end
|
#sections ⇒ Object
47
48
49
|
# File 'lib/para/components_configuration.rb', line 47
def sections
@sections ||= []
end
|
#sections_ids_hash ⇒ Object
160
161
162
|
# File 'lib/para/components_configuration.rb', line 160
def sections_ids_hash
@sections_ids_hash ||= {}.with_indifferent_access
end
|