Module: Padrino::Reloader

Defined in:
lib/vendored-middleman-deps/padrino-core-0.11.2/lib/padrino-core/reloader.rb

Overview

High performance source code reloader middleware

Defined Under Namespace

Classes: Rack

Constant Summary collapse

MTIMES =

The modification times for every file in a project.

{}
LOADED_FILES =

The list of files loaded as part of a project.

{}
LOADED_CLASSES =

The list of object constants and classes loaded as part of the project.

{}

Class Method Summary collapse

Class Method Details

.changed?Boolean Also known as: run!

Returns true if any file changes are detected and populates the MTIMES cache

Returns:

  • (Boolean)


85
86
87
88
89
90
91
92
93
# File 'lib/vendored-middleman-deps/padrino-core-0.11.2/lib/padrino-core/reloader.rb', line 85

def changed?
  changed = false
  rotation do |file, mtime|
    new_file = MTIMES[file].nil?
    previous_mtime = MTIMES[file]
    changed = true if new_file || mtime > previous_mtime
  end
  changed
end

.clear!Object

Remove files and classes loaded with stat



76
77
78
79
80
# File 'lib/vendored-middleman-deps/padrino-core-0.11.2/lib/padrino-core/reloader.rb', line 76

def clear!
  clear_modification_times
  clear_loaded_classes
  clear_loaded_files_and_features
end

.excludeObject

Specified folders can be excluded from the code reload detection process. Default excluded directories at Padrino.root are: test, spec, features, tmp, config, db and public



28
29
30
# File 'lib/vendored-middleman-deps/padrino-core-0.11.2/lib/padrino-core/reloader.rb', line 28

def exclude
  @_exclude ||= %w(test spec tmp features config public db).map { |path| Padrino.root(path) }
end

.exclude_constantsObject

Specified constants can be excluded from the code unloading process.



35
36
37
# File 'lib/vendored-middleman-deps/padrino-core-0.11.2/lib/padrino-core/reloader.rb', line 35

def exclude_constants
  @_exclude_constants ||= Set.new
end

.figure_path(file) ⇒ Object

Returns true if the file is defined in our padrino root



159
160
161
162
163
164
165
166
# File 'lib/vendored-middleman-deps/padrino-core-0.11.2/lib/padrino-core/reloader.rb', line 159

def figure_path(file)
  return file if Pathname.new(file).absolute?
  $:.each do |path|
    found = File.join(path, file)
    return File.expand_path(found) if File.exist?(found)
  end
  file
end

.include_constantsObject

Specified constants can be configured to be reloaded on every request. Default included constants are: [none]



43
44
45
# File 'lib/vendored-middleman-deps/padrino-core-0.11.2/lib/padrino-core/reloader.rb', line 43

def include_constants
  @_include_constants ||= Set.new
end

.lock!Object

We lock dependencies sets to prevent reloading of protected constants



99
100
101
102
103
104
105
106
# File 'lib/vendored-middleman-deps/padrino-core-0.11.2/lib/padrino-core/reloader.rb', line 99

def lock!
  klasses = ObjectSpace.classes do |klass|
    klass._orig_klass_name.split('::')[0]
  end

  klasses = klasses | Padrino.mounted_apps.map { |app| app.app_class }
  Padrino::Reloader.exclude_constants.merge(klasses)
end

.reload!Object

Reload all files with changes detected.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/vendored-middleman-deps/padrino-core-0.11.2/lib/padrino-core/reloader.rb', line 50

def reload!
  # Detect changed files
  rotation do |file, mtime|
    # Retrive the last modified time
    new_file       = MTIMES[file].nil?
    previous_mtime = MTIMES[file] ||= mtime
    logger.devel "Detected a new file #{file}" if new_file
    # We skip to next file if it is not new and not modified
    next unless new_file || mtime > previous_mtime
    # Now we can reload our file
    apps = mounted_apps_of(file)
    if apps.present?
      apps.each { |app| app.app_obj.reload! }
    else
      safe_load(file, :force => new_file)
      # Reload also apps
      Padrino.mounted_apps.each do |app|
        app.app_obj.reload! if app.app_obj.dependencies.include?(file)
      end
    end
  end
end

.remove_constant(const) ⇒ Object

Removes the specified class and constant.



171
172
173
174
175
176
177
178
179
180
181
# File 'lib/vendored-middleman-deps/padrino-core-0.11.2/lib/padrino-core/reloader.rb', line 171

def remove_constant(const)
  return if exclude_constants.any? { |c| const._orig_klass_name.index(c) == 0 } &&
           !include_constants.any? { |c| const._orig_klass_name.index(c) == 0 }
  begin
    parts  = const.to_s.sub(/^::(Object)?/, 'Object::').split('::')
    object = parts.pop
    base   = parts.empty? ? Object : Inflector.constantize(parts * '::')
    base.send :remove_const, object
    logger.devel "Removed constant: #{const} from #{base}"
  rescue NameError; end
end

.safe_load(file, options = {}) ⇒ Object

A safe Kernel::require which issues the necessary hooks depending on results



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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
# File 'lib/vendored-middleman-deps/padrino-core-0.11.2/lib/padrino-core/reloader.rb', line 111

def safe_load(file, options={})
  began_at = Time.now
  force    = options[:force]
  file     = figure_path(file)
  reload   = should_reload?(file)
  m_time   = modification_time(file)

  return if !force && m_time && !reload

  remove_loaded_file_classes(file)
  remove_loaded_file_features(file)

  # Duplicate objects and loaded features before load file
  klasses = ObjectSpace.classes
  files   = Set.new($LOADED_FEATURES.dup)

  reload_deps_of_file(file)

  # And finally load the specified file
  begin
    logger.devel :loading, began_at, file if !reload
    logger.debug :reload,  began_at, file if  reload

    $LOADED_FEATURES.delete(file) if files.include?(file)
    Padrino::Utils.silence_output
    loaded = false
    require(file)
    loaded = true
    update_modification_time(file)
  rescue SyntaxError => e
    logger.error "Cannot require #{file} due to a syntax error: #{e.message}"
  ensure
    Padrino::Utils.unsilence_output
    new_constants = ObjectSpace.new_classes(klasses)
    if loaded
      process_loaded_file(:file      => file, 
                          :constants => new_constants, 
                          :files     => files)
    else
      logger.devel "Failed to load #{file}; removing partially defined constants"
      unload_constants(new_constants)
    end
  end
end