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.3.0'
Instance Attribute Summary collapse
-
#default_delay ⇒ Object
readonly
Returns the value of attribute default_delay.
-
#default_onchange ⇒ Object
readonly
Returns the value of attribute default_onchange.
-
#reloadable_paths ⇒ Object
Returns the value of attribute reloadable_paths.
Instance Method Summary collapse
- #activate(reloadable_paths: [], onchange: true, delay: nil, watch_paths: nil, watch_latency: 1, sync_require: false) ⇒ 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) ⇒ 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.
32 33 34 |
# File 'lib/auto_reloader.rb', line 32 def initialize @activate_lock = Mutex.new end |
Instance Attribute Details
#default_delay ⇒ Object (readonly)
Returns the value of attribute default_delay.
15 16 17 |
# File 'lib/auto_reloader.rb', line 15 def default_delay @default_delay end |
#default_onchange ⇒ Object (readonly)
Returns the value of attribute default_onchange.
15 16 17 |
# File 'lib/auto_reloader.rb', line 15 def default_onchange @default_onchange end |
#reloadable_paths ⇒ Object
Returns the value of attribute reloadable_paths.
15 16 17 |
# File 'lib/auto_reloader.rb', line 15 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) ⇒ Object
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/auto_reloader.rb', line 37 def activate(reloadable_paths: [], onchange: true, delay: nil, watch_paths: nil, watch_latency: 1, sync_require: false) @activate_lock.synchronize do raise ActivatedMoreThanOnce, 'Can only activate Autoreloader once' if @reloadable_paths @default_delay = delay @default_onchange = onchange @watch_latency = watch_latency sync_require! if sync_require @reload_lock = Mutex.new @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.
71 72 73 |
# File 'lib/auto_reloader.rb', line 71 def async_require! @require_lock = nil end |
#force_next_reload ⇒ Object
151 152 153 |
# File 'lib/auto_reloader.rb', line 151 def force_next_reload @force_reload = true end |
#maybe_synchronize(&block) ⇒ Object
112 113 114 |
# File 'lib/auto_reloader.rb', line 112 def maybe_synchronize(&block) @require_lock ? @require_lock.synchronize(&block) : yield end |
#reload!(delay: default_delay, onchange: default_onchange, watch_paths: @watch_paths) ⇒ Object
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/auto_reloader.rb', line 121 def reload!(delay: default_delay, onchange: default_onchange, watch_paths: @watch_paths) if onchange && !block_given? raise InvalidUsage, 'A block must be provided to reload! when onchange is true (the default)' end unload! unless reload_ignored = ignore_reload?(delay, onchange, watch_paths) result = nil if block_given? result = yield find_mtime end @last_reloaded = clock_time if delay result end |
#require(path, &block) ⇒ Object
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/auto_reloader.rb', line 80 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
116 117 118 |
# File 'lib/auto_reloader.rb', line 116 def require_relative(path, fullpath) require(fullpath){ Kernel.require fullpath } end |
#stop_listener ⇒ Object
147 148 149 |
# File 'lib/auto_reloader.rb', line 147 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.
62 63 64 |
# File 'lib/auto_reloader.rb', line 62 def sync_require! @require_lock ||= Monitor.new # monitor is like Mutex, but reentrant end |
#unload! ⇒ Object
137 138 139 140 141 142 143 144 145 |
# File 'lib/auto_reloader.rb', line 137 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 |