class Camping::Reloader

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.

Attributes

file[R]

Public Class Methods

new(file, &blk) click to toggle source
# File lib/camping/reloader.rb, line 37
def initialize(file, &blk)
  @file = file
  @mtime = Time.at(0)
  @requires = []
  @apps = {}
  @callback = blk
end

Public Instance Methods

==(other) click to toggle source

Checks if both scripts watches the same file.

# File lib/camping/reloader.rb, line 128
def ==(other)
  @file == other.file
end
apps() click to toggle source
# File lib/camping/reloader.rb, line 132
def apps
  if @app
    { name => @app }
  else
    @apps
  end
end
load_apps(old_apps) click to toggle source

Loads the apps available in this script. Use apps to get the loaded apps.

# File lib/camping/reloader.rb, line 56
def load_apps(old_apps)
  all_requires = $LOADED_FEATURES.dup
  all_apps = Camping::Apps.dup

  load_file
ensure
  @requires = []
  dirs = []
  new_apps = Camping::Apps - all_apps

  @apps = new_apps.inject({}) do |hash, app|
    if file = app.options[:__FILE__]
      full = File.expand_path(file)
      @requires << [file, full]
      dirs << full.sub(/\.[^.]+$/, '')
    end

    key = app.name.to_sym
    hash[key] = app

    if !old_apps.include?(key)
      @callback.call(app) if @callback
      app.create if app.respond_to?(:create)
    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() click to toggle source
# File lib/camping/reloader.rb, line 94
def load_file
  if @file =~ /\.ru$/
    @app,_ = Rack::Builder.parse_file(@file)
  else
    load(@file)
  end
end
name() click to toggle source
# File lib/camping/reloader.rb, line 45
def name
  @name ||= begin
    base = @file.dup
    base = File.dirname(base) if base =~ /\bconfig\.ru$/
    base.sub!(/\.[^.]+/, '')
    File.basename(base).to_sym
  end
end
reload() click to toggle source

Reloads the file if needed. No harm is done by calling this multiple times, so feel free call just to be sure.

# File lib/camping/reloader.rb, line 118
def reload
  return if @mtime >= mtime rescue nil
  reload!
end
reload!() click to toggle source
# File lib/camping/reloader.rb, line 123
def reload!
  load_apps(remove_apps)
end
remove_apps() click to toggle source

Removes all the apps defined in this script.

# File lib/camping/reloader.rb, line 103
def remove_apps
  @requires.each do |(path, full)|
    $LOADED_FEATURES.delete(path)
  end

  @apps.each do |name, app|
    Camping::Apps.delete(app)
    Object.send :remove_const, name
  end.dup
ensure
  @apps.clear
end