Class: RailsDevelopmentBoost::LoadedFile

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

Direct Known Subclasses

RoutesLoadedFile

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.



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

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

Instance Attribute Details

#constantsObject

Returns the value of attribute constants.



124
125
126
# File 'lib/rails_development_boost/loaded_file.rb', line 124

def constants
  @constants
end

#pathObject

Returns the value of attribute path.



124
125
126
# File 'lib/rails_development_boost/loaded_file.rb', line 124

def path
  @path
end

Class Method Details

.const_unloaded(const_name) ⇒ Object



275
276
277
# File 'lib/rails_development_boost/loaded_file.rb', line 275

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



251
252
253
# File 'lib/rails_development_boost/loaded_file.rb', line 251

def for(file_path)
  LOADED[file_path]
end

.loaded?(file_path) ⇒ Boolean

Returns:

  • (Boolean)


255
256
257
# File 'lib/rails_development_boost/loaded_file.rb', line 255

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

.loaded_constant?(const_name) ⇒ Boolean

Returns:

  • (Boolean)


263
264
265
# File 'lib/rails_development_boost/loaded_file.rb', line 263

def loaded_constant?(const_name)
  CONSTANTS_TO_FILES[const_name]
end

.loaded_constantsObject



259
260
261
# File 'lib/rails_development_boost/loaded_file.rb', line 259

def loaded_constants
  LOADED.constants
end

.relate_files(base_file, related_file) ⇒ Object



279
280
281
# File 'lib/rails_development_boost/loaded_file.rb', line 279

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

.schedule_containing_file(const_name, file) ⇒ Object



271
272
273
# File 'lib/rails_development_boost/loaded_file.rb', line 271

def schedule_containing_file(const_name, file)
  file.schedule_consts_for_unloading!
end

.schedule_for_unloading_files_with_const!(const_name) ⇒ Object



267
268
269
# File 'lib/rails_development_boost/loaded_file.rb', line 267

def schedule_for_unloading_files_with_const!(const_name)
  CONSTANTS_TO_FILES.each_file_with_const(const_name) {|file| schedule_containing_file(const_name, file)}
end

.unload_modified!Object



247
248
249
# File 'lib/rails_development_boost/loaded_file.rb', line 247

def unload_modified!
  LOADED.unload_modified!
end

Instance Method Details

#add_constants(new_constants) ⇒ Object



137
138
139
140
# File 'lib/rails_development_boost/loaded_file.rb', line 137

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



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

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



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

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

#changed?Boolean

Returns:

  • (Boolean)


132
133
134
135
# File 'lib/rails_development_boost/loaded_file.rb', line 132

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

#clean_up_if_necessaryObject



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

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)


148
149
150
# File 'lib/rails_development_boost/loaded_file.rb', line 148

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

#delete_constant(const_name) ⇒ Object



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

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

#dependent_file_schedule_for_unloading!(dependent_file) ⇒ Object



202
203
204
# File 'lib/rails_development_boost/loaded_file.rb', line 202

def dependent_file_schedule_for_unloading!(dependent_file)
  dependent_file.schedule_consts_for_unloading!
end

#dirnameObject



142
143
144
# File 'lib/rails_development_boost/loaded_file.rb', line 142

def dirname
  File.dirname(@path)
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


198
199
200
# File 'lib/rails_development_boost/loaded_file.rb', line 198

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`.



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/rails_development_boost/loaded_file.rb', line 173

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



206
207
208
209
210
211
212
213
214
# File 'lib/rails_development_boost/loaded_file.rb', line 206

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

#hashObject

consistent hashing



194
195
196
# File 'lib/rails_development_boost/loaded_file.rb', line 194

def hash
  @path.hash
end

#read_greppable_constantsObject



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

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

#require_pathObject



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

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

#schedule_const_for_unloading(const_name) ⇒ Object



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

def schedule_const_for_unloading(const_name)
  ActiveSupport::Dependencies.schedule_const_for_unloading(const_name)
end

#schedule_consts_for_unloading!Object



152
153
154
155
156
157
158
# File 'lib/rails_development_boost/loaded_file.rb', line 152

def schedule_consts_for_unloading!
  guard_double_unloading do
    INTERDEPENDENCIES.each_dependent_on(self) {|dependent_file| dependent_file_schedule_for_unloading!(dependent_file)}
    @constants.dup.each {|const| schedule_const_for_unloading(const)}
    clean_up_if_necessary
  end
end

#stale!Object



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

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