Class: AutoReloader
- Inherits:
-
Object
- Object
- AutoReloader
- Extended by:
- SingleForwardable
- Includes:
- Singleton
- Defined in:
- lib/auto_reloader.rb,
lib/auto_reloader/version.rb
Defined Under Namespace
Modules: RequireOverride
Constant Summary collapse
- ActivatedMoreThanOnce =
Class.new RuntimeError
- InvalidUsage =
Class.new RuntimeError
- VERSION =
'0.4.1'
Instance Attribute Summary collapse
-
#default_await_before_unload ⇒ Object
readonly
default_await_before_unload will await for all calls to reload! to finish before calling unload!.
-
#default_delay ⇒ Object
readonly
default_await_before_unload will await for all calls to reload! to finish before calling unload!.
-
#default_onchange ⇒ Object
readonly
default_await_before_unload will await for all calls to reload! to finish before calling unload!.
-
#reloadable_paths ⇒ Object
default_await_before_unload will await for all calls to reload! to finish before calling unload!.
Instance Method Summary collapse
- #activate(reloadable_paths: [], onchange: true, delay: nil, watch_paths: nil, watch_latency: 1, sync_require: false, await_before_unload: true) ⇒ Object
-
#async_require! ⇒ Object
See the documentation for sync_require! to understand the reasoning.
- #force_next_reload ⇒ Object
-
#initialize ⇒ AutoReloader
constructor
A new instance of AutoReloader.
- #maybe_synchronize(&block) ⇒ Object
- #reload!(delay: default_delay, onchange: default_onchange, watch_paths: @watch_paths, await_before_unload: default_await_before_unload) ⇒ Object
- #require(path, &block) ⇒ Object
- #require_relative(path, fullpath) ⇒ Object
- #stop_listener ⇒ Object
-
#sync_require! ⇒ Object
when concurrent threads require files race conditions may prevent the automatic detection of constants created by a given file.
- #unload! ⇒ Object
Constructor Details
#initialize ⇒ AutoReloader
Returns a new instance of AutoReloader.
37 38 39 |
# File 'lib/auto_reloader.rb', line 37 def initialize @activate_lock = Mutex.new end |
Instance Attribute Details
#default_await_before_unload ⇒ Object (readonly)
default_await_before_unload will await for all calls to reload! to finish before calling unload!. This behavior is usually desired in web applications to avoid unloading anything while a request hasn’t been finished, however it won’t work fine if some requests are supposed to remain open, like websockets connections or something like that.
20 21 22 |
# File 'lib/auto_reloader.rb', line 20 def default_await_before_unload @default_await_before_unload end |
#default_delay ⇒ Object (readonly)
default_await_before_unload will await for all calls to reload! to finish before calling unload!. This behavior is usually desired in web applications to avoid unloading anything while a request hasn’t been finished, however it won’t work fine if some requests are supposed to remain open, like websockets connections or something like that.
20 21 22 |
# File 'lib/auto_reloader.rb', line 20 def default_delay @default_delay end |
#default_onchange ⇒ Object (readonly)
default_await_before_unload will await for all calls to reload! to finish before calling unload!. This behavior is usually desired in web applications to avoid unloading anything while a request hasn’t been finished, however it won’t work fine if some requests are supposed to remain open, like websockets connections or something like that.
20 21 22 |
# File 'lib/auto_reloader.rb', line 20 def default_onchange @default_onchange end |
#reloadable_paths ⇒ Object
default_await_before_unload will await for all calls to reload! to finish before calling unload!. This behavior is usually desired in web applications to avoid unloading anything while a request hasn’t been finished, however it won’t work fine if some requests are supposed to remain open, like websockets connections or something like that.
20 21 22 |
# File 'lib/auto_reloader.rb', line 20 def reloadable_paths @reloadable_paths end |
Instance Method Details
#activate(reloadable_paths: [], onchange: true, delay: nil, watch_paths: nil, watch_latency: 1, sync_require: false, await_before_unload: true) ⇒ Object
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/auto_reloader.rb', line 42 def activate(reloadable_paths: [], onchange: true, delay: nil, watch_paths: nil, watch_latency: 1, sync_require: false, await_before_unload: true) @activate_lock.synchronize do raise ActivatedMoreThanOnce, 'Can only activate Autoreloader once' if @reloadable_paths @default_delay = delay @default_onchange = onchange @default_await_before_unload = await_before_unload @watch_latency = watch_latency sync_require! if sync_require @reload_lock = Mutex.new @zero_requests_condition = ConditionVariable.new @requests_count = 0 @top_level_consts_stack = [] @unload_constants = Set.new @unload_files = Set.new @last_reloaded = clock_time try_listen unless watch_paths == false self.reloadable_paths = reloadable_paths Object.include RequireOverride end end |
#async_require! ⇒ Object
See the documentation for sync_require! to understand the reasoning. Async require is the default behavior but could lead to race conditions. If you know your requires will never block it may be a good idea to call sync_require!. If you know what require will block you can call async_require!, require it, and then call sync_require! which will generate a new monitor.
79 80 81 |
# File 'lib/auto_reloader.rb', line 79 def async_require! @require_lock = nil end |
#force_next_reload ⇒ Object
173 174 175 |
# File 'lib/auto_reloader.rb', line 173 def force_next_reload @force_reload = true end |
#maybe_synchronize(&block) ⇒ Object
120 121 122 |
# File 'lib/auto_reloader.rb', line 120 def maybe_synchronize(&block) @require_lock ? @require_lock.synchronize(&block) : yield end |
#reload!(delay: default_delay, onchange: default_onchange, watch_paths: @watch_paths, await_before_unload: default_await_before_unload) ⇒ Object
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/auto_reloader.rb', line 129 def reload!(delay: default_delay, onchange: default_onchange, watch_paths: @watch_paths, await_before_unload: default_await_before_unload) if onchange && !block_given? raise InvalidUsage, 'A block must be provided to reload! when onchange is true (the default)' end unless reload_ignored = ignore_reload?(delay, onchange, watch_paths) @reload_lock.synchronize do @zero_requests_condition.wait(@reload_lock) unless @requests_count == 0 end if await_before_unload && block_given? unload! end result = nil if block_given? @reload_lock.synchronize{ @requests_count += 1 } begin result = yield !reload_ignored ensure @reload_lock.synchronize{ @requests_count -= 1 @zero_requests_condition.signal if @requests_count == 0 } end find_mtime end @last_reloaded = clock_time if delay result end |
#require(path, &block) ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/auto_reloader.rb', line 88 def require(path, &block) was_required = false error = nil maybe_synchronize do @top_level_consts_stack << Set.new old_consts = Object.constants prev_consts = new_top_level_constants = nil begin was_required = yield rescue Exception => e error = e ensure prev_consts = @top_level_consts_stack.pop return false if !error && !was_required # was required already, do nothing new_top_level_constants = Object.constants - old_consts - prev_consts.to_a (new_top_level_constants.each{|c| safe_remove_constant c }; raise error) if error @top_level_consts_stack.each{|c| c.merge new_top_level_constants } full_loaded_path = $LOADED_FEATURES.last return was_required unless reloadable? full_loaded_path, path @reload_lock.synchronize do @unload_constants.merge new_top_level_constants @unload_files << full_loaded_path end end end was_required end |
#require_relative(path, fullpath) ⇒ Object
124 125 126 |
# File 'lib/auto_reloader.rb', line 124 def require_relative(path, fullpath) require(fullpath){ Kernel.require fullpath } end |
#stop_listener ⇒ Object
169 170 171 |
# File 'lib/auto_reloader.rb', line 169 def stop_listener @listener.stop if @listener end |
#sync_require! ⇒ Object
when concurrent threads require files race conditions may prevent the automatic detection of constants created by a given file. Calling sync_require! will ensure only a single file is required at a single time. However, if a required file blocks (think of a web server) then any requires by a separate thread would be blocked forever (or until the web server shutdowns). That’s why require is async by default even though it would be vulnerable to race conditions.
70 71 72 |
# File 'lib/auto_reloader.rb', line 70 def sync_require! @require_lock ||= Monitor.new # monitor is like Mutex, but reentrant end |
#unload! ⇒ Object
159 160 161 162 163 164 165 166 167 |
# File 'lib/auto_reloader.rb', line 159 def unload! @force_reload = false @reload_lock.synchronize do @unload_files.each{|f| $LOADED_FEATURES.delete f } @unload_constants.each{|c| safe_remove_constant c } @unload_files = Set.new @unload_constants = Set.new end end |