Class: RailsDevelopmentBoost::LoadedFile

Inherits:
Object
  • Object
show all
Defined in:
lib/rails_development_boost/loaded_file.rb

Defined Under Namespace

Classes: ConstantsToFiles, Files, Interdependencies

Constant Summary collapse

LOADED =
Files.new
CONSTANTS_TO_FILES =
ConstantsToFiles.new
INTERDEPENDENCIES =
Interdependencies.new
NOW_UNLOADING =
Set.new

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path, constants = []) ⇒ LoadedFile

Returns a new instance of LoadedFile.



97
98
99
100
101
# File 'lib/rails_development_boost/loaded_file.rb', line 97

def initialize(path, constants=[])
  @path       = path
  @constants  = constants
  @mtime      = current_mtime
end

Instance Attribute Details

#constantsObject

Returns the value of attribute constants.



95
96
97
# File 'lib/rails_development_boost/loaded_file.rb', line 95

def constants
  @constants
end

#pathObject

Returns the value of attribute path.



95
96
97
# File 'lib/rails_development_boost/loaded_file.rb', line 95

def path
  @path
end

Class Method Details

.const_unloaded(const_name) ⇒ Object



238
239
240
# File 'lib/rails_development_boost/loaded_file.rb', line 238

def const_unloaded(const_name)
  CONSTANTS_TO_FILES.each_file_with_const(const_name) {|file| file.delete_constant(const_name)}
end

.for(file_path) ⇒ Object



214
215
216
# File 'lib/rails_development_boost/loaded_file.rb', line 214

def for(file_path)
  LOADED[file_path]
end

.loaded?(file_path) ⇒ Boolean

Returns:

  • (Boolean)


218
219
220
# File 'lib/rails_development_boost/loaded_file.rb', line 218

def loaded?(file_path)
  LOADED.loaded?(file_path)
end

.loaded_constant?(const_name) ⇒ Boolean

Returns:

  • (Boolean)


226
227
228
# File 'lib/rails_development_boost/loaded_file.rb', line 226

def loaded_constant?(const_name)
  CONSTANTS_TO_FILES[const_name]
end

.loaded_constantsObject



222
223
224
# File 'lib/rails_development_boost/loaded_file.rb', line 222

def loaded_constants
  LOADED.constants
end

.relate_files(base_file, related_file) ⇒ Object



242
243
244
# File 'lib/rails_development_boost/loaded_file.rb', line 242

def relate_files(base_file, related_file)
  LOADED[base_file].associate_with(LOADED[related_file])
end

.unload_containing_file(const_name, file) ⇒ Object



234
235
236
# File 'lib/rails_development_boost/loaded_file.rb', line 234

def unload_containing_file(const_name, file)
  file.unload!
end

.unload_files_with_const!(const_name) ⇒ Object



230
231
232
# File 'lib/rails_development_boost/loaded_file.rb', line 230

def unload_files_with_const!(const_name)
  CONSTANTS_TO_FILES.each_file_with_const(const_name) {|file| unload_containing_file(const_name, file)}
end

.unload_modified!Object



210
211
212
# File 'lib/rails_development_boost/loaded_file.rb', line 210

def unload_modified!
  LOADED.unload_modified!
end

Instance Method Details

#add_constants(new_constants) ⇒ Object



108
109
110
111
# File 'lib/rails_development_boost/loaded_file.rb', line 108

def add_constants(new_constants)
  new_constants.each {|new_constant| CONSTANTS_TO_FILES.associate(new_constant, self)}
  @constants |= new_constants
end

#associate_to_greppable_constantsObject

brute-force approach



127
128
129
130
131
# File 'lib/rails_development_boost/loaded_file.rb', line 127

def associate_to_greppable_constants # brute-force approach
  # we don't know anything about the constants contained in the files up the currently_loading stack
  ActiveSupport::Dependencies.currently_loading.each {|path| self.class.relate_files(path, self)}
  add_constants(greppable_constants)
end

#associate_with(other_loaded_file) ⇒ Object



196
197
198
# File 'lib/rails_development_boost/loaded_file.rb', line 196

def associate_with(other_loaded_file)
  INTERDEPENDENCIES.associate(self, other_loaded_file)
end

#changed?Boolean

Returns:

  • (Boolean)


103
104
105
106
# File 'lib/rails_development_boost/loaded_file.rb', line 103

def changed?
  previous_mtime, @mtime = @mtime, current_mtime
  previous_mtime != @mtime
end

#clean_up_if_necessaryObject



189
190
191
192
193
194
# File 'lib/rails_development_boost/loaded_file.rb', line 189

def clean_up_if_necessary
  if @constants.empty? && LOADED.stored?(self)
    LOADED.delete(@path)
    ActiveSupport::Dependencies.loaded.delete(require_path)
  end
end

#decorator_like?Boolean

“decorator” files are popular with certain Rails frameworks (spree/refinerycms etc.) they don’t define their own constants, instead they are usually used for adding methods to other classes via Model.class_eval { def meth; end }

Returns:

  • (Boolean)


115
116
117
# File 'lib/rails_development_boost/loaded_file.rb', line 115

def decorator_like?
  @constants.empty? && !INTERDEPENDENCIES[self]
end

#delete_constant(const_name) ⇒ Object



183
184
185
186
187
# File 'lib/rails_development_boost/loaded_file.rb', line 183

def delete_constant(const_name)
  CONSTANTS_TO_FILES.deassociate(const_name, self)
  @constants.delete(const_name)
  clean_up_if_necessary
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


165
166
167
# File 'lib/rails_development_boost/loaded_file.rb', line 165

def eql?(other)
  @path.eql?(other)
end

#greppable_constantsObject

It is important to catch all the intermediate constants as they might be “nested” constants, that are generally not tracked by AS::Dependencies. Pathological example is as follows:

File ‘a.rb` contains `class A; X = :x; end`. AS::Dependencies only associates the ’A’ const to the ‘a.rb` file (ignoring the nested ’A::X’), while a decorator file ‘b_decorator.rb` containing `B.class_eval A::X` would grep and find the `A::X` const, check that indeed it is in autoloaded namespace and associate it to itself. When `b_decorator.rb` is then being unloaded it simply does `remove_constant(’A::X’)‘ while failing to trigger the unloading of `a.rb`.



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/rails_development_boost/loaded_file.rb', line 140

def greppable_constants
  constants = []
  read_greppable_constants.each do |const_name|
    intermediates = nil
    begin
      if self.class.loaded_constant?(const_name)
        constants << const_name
        constants.concat(intermediates) if intermediates
        break
      end
      (intermediates ||= []) << const_name.dup
    end while const_name.sub!(/::[^:]+\Z/, '')
  end
  constants.uniq
end

#guard_double_unloadingObject



173
174
175
176
177
178
179
180
181
# File 'lib/rails_development_boost/loaded_file.rb', line 173

def guard_double_unloading
  if NOW_UNLOADING.add?(self)
    begin
      yield
    ensure
      NOW_UNLOADING.delete(self)
    end
  end
end

#hashObject

consistent hashing



161
162
163
# File 'lib/rails_development_boost/loaded_file.rb', line 161

def hash
  @path.hash
end

#read_greppable_constantsObject



156
157
158
# File 'lib/rails_development_boost/loaded_file.rb', line 156

def read_greppable_constants
  File.read(@path).scan(/[A-Z][_A-Za-z0-9]*(?:::[A-Z][_A-Za-z0-9]*)*/).uniq
end

#require_pathObject



200
201
202
# File 'lib/rails_development_boost/loaded_file.rb', line 200

def require_path
  File.expand_path(@path.sub(/\.rb\Z/, '')) # be sure to do the same thing as Dependencies#require_or_load and use the expanded path
end

#stale!Object



204
205
206
207
# File 'lib/rails_development_boost/loaded_file.rb', line 204

def stale!
  @mtime = 0
  INTERDEPENDENCIES.each_dependent_on(self, &:stale!)
end

#unload!Object



119
120
121
122
123
124
125
# File 'lib/rails_development_boost/loaded_file.rb', line 119

def unload!
  guard_double_unloading do
    INTERDEPENDENCIES.each_dependent_on(self) {|dependent_file| unload_dependent_file(dependent_file)}
    @constants.dup.each {|const| ActiveSupport::Dependencies.remove_constant(const)}
    clean_up_if_necessary
  end
end

#unload_dependent_file(dependent_file) ⇒ Object



169
170
171
# File 'lib/rails_development_boost/loaded_file.rb', line 169

def unload_dependent_file(dependent_file)
  dependent_file.unload!
end