Class: Camping::Loader
Overview
The Camping Reloader
Camping apps are generally small and predictable. Many Camping apps are contained within a single file. Larger apps are split into a handful of other Ruby libraries within the same directory.
Since Camping apps (and their dependencies) are loaded with Ruby’s require method, there is a record of them in $LOADED_FEATURES. Which leaves a perfect space for this class to manage auto-reloading an app if any of its immediate dependencies changes.
Wrapping Your Apps
Since bin/camping and the Camping::Server class already use the Reloader, you probably don’t need to hack it on your own. But, if you’re rolling your own situation, here’s how.
Rather than this:
require 'yourapp'
Use this:
require 'camping/reloader'
reloader = Camping::Reloader.new('/path/to/yourapp.rb')
blog = reloader.apps[:Blog]
wiki = reloader.apps[:Wiki]
The blog
and wiki
objects will behave exactly like your Blog and Wiki, but they will update themselves if yourapp.rb changes.
You can also give Reloader more than one script.
Constant Summary collapse
- Loaders =
[]
Instance Attribute Summary collapse
-
#file ⇒ Object
readonly
Returns the value of attribute file.
Instance Method Summary collapse
-
#==(other) ⇒ Object
Checks if both scripts watches the same file.
- #apps ⇒ Object
-
#initialize(file = nil, &blk) ⇒ Loader
constructor
A new instance of Loader.
-
#load_everything ⇒ Object
remove_constants called inside this.
-
#load_file ⇒ Object
load_file.
- #name ⇒ Object
- #pause ⇒ Object
-
#processing_events? ⇒ Boolean
pass through methods to the Listener.
-
#reload ⇒ Object
Reloads the file if needed.
-
#reload! ⇒ Object
Force a reload.
-
#remove_constants ⇒ Object
removes all constants recursively included using this script as a root.
- #start ⇒ Object
- #stop ⇒ Object
Constructor Details
#initialize(file = nil, &blk) ⇒ Loader
Returns a new instance of Loader.
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/camping/loader.rb', line 41 def initialize(file=nil, &blk) @file = file @mtime = Time.at(0) @requires = [] @apps = {} @callback = blk @root = Dir.pwd @file = @root + '/camp.rb' if @file == nil @zeit = Zeitwerk::Loader.new Loaders << @zeit # setup Zeit for this reloader setup_zeit(@zeit) dirs = [@root] dirs << "#{@root}/apps" if Dir.exist? "#{@root}/apps" dirs << "#{@root}/lib" if Dir.exist? "#{@root}/lib" # setup recursive listener on the apps and lib directories from the source script. @listener = Listen.to(*dirs) do |modified, added, removed| @mtime = Time.now reload! end start end |
Instance Attribute Details
#file ⇒ Object (readonly)
Returns the value of attribute file.
38 39 40 |
# File 'lib/camping/loader.rb', line 38 def file @file end |
Instance Method Details
#==(other) ⇒ Object
Checks if both scripts watches the same file.
174 175 176 |
# File 'lib/camping/loader.rb', line 174 def ==(other) @file == other.file end |
#apps ⇒ Object
178 179 180 181 182 183 184 |
# File 'lib/camping/loader.rb', line 178 def apps if @app { name => @app } else @apps end end |
#load_everything ⇒ Object
remove_constants called inside this.
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 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/camping/loader.rb', line 84 def load_everything() all_requires = $LOADED_FEATURES.dup all_apps = Camping::Apps.dup # Zeitwerk will Autoload stuff, which is great. But we don't want Zeitwerk # autoloading when we evaluate the camp.rb file because it will try to # autoload any controllers and helpers that we've defined in there from # the descendant /apps and /lib directory, which then make it break. @zeit.unload load_file @zeit.setup reload_directory("#{@root}/apps") reload_directory("#{@root}/lib") Camping.make_camp ensure @requires = [] new_apps = Camping::Apps - all_apps @apps = new_apps.inject({}) do |hash, app| if file = app.[:__FILE__] full = File.(file) @requires << [file, full] end key = app.name.to_sym hash[key] = app if !@apps.include?(key) @callback.call(app) if @callback app.create if app.respond_to?(:create) app.kindling if app.respond_to?(:kindling) end hash end ($LOADED_FEATURES - all_requires).each do |req| full = full_path(req) @requires << [req, full] # if dirs.any? { |x| full.index(x) == 0 } end @mtime = mtime self end |
#load_file ⇒ Object
load_file
Rack::Builder is mainly used to parse a config.ru file and to build a rack app with middleware from that.
135 136 137 138 139 140 141 142 |
# File 'lib/camping/loader.rb', line 135 def load_file if @file =~ /\.ru$/ @app = Rack::Builder.parse_file(@file) else load(@file) end @requires << [@file, File.(@file)] end |
#name ⇒ Object
74 75 76 77 78 79 80 81 |
# File 'lib/camping/loader.rb', line 74 def name @name ||= begin base = @file.dup base = File.dirname(base) if base =~ /\bconfig\.ru$/ base.sub!(/\.[^.]+/, '') File.basename(base).to_sym end end |
#pause ⇒ Object
71 |
# File 'lib/camping/loader.rb', line 71 def pause = @listener.pause |
#processing_events? ⇒ Boolean
pass through methods to the Listener. for testing purposes.
69 |
# File 'lib/camping/loader.rb', line 69 def processing_events? = @listener.processing? |
#reload ⇒ Object
Reloads the file if needed. No harm is done by calling this multiple times, so feel free call just to be sure.
162 163 164 165 |
# File 'lib/camping/loader.rb', line 162 def reload return if @mtime >= mtime rescue nil reload! end |
#reload! ⇒ Object
Force a reload.
168 169 170 171 |
# File 'lib/camping/loader.rb', line 168 def reload! remove_constants load_everything end |
#remove_constants ⇒ Object
removes all constants recursively included using this script as a root. so everything in /apps, and /lib in relation from this script.
146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/camping/loader.rb', line 146 def remove_constants @requires.each do |(path, full)| $LOADED_FEATURES.delete(path) unless path.match? "concurrent-ruby" end @apps.each do |name, app| Camping::Apps.delete(app) Object.send :remove_const, name end.dup ensure @apps.clear @requires.clear end |
#start ⇒ Object
72 |
# File 'lib/camping/loader.rb', line 72 def start = @listener.start |
#stop ⇒ Object
70 |
# File 'lib/camping/loader.rb', line 70 def stop = @listener.stop |