class_loader 3.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +1 -1
- data/lib/class_loader/class_loader.rb +54 -48
- data/lib/class_loader/watcher.rb +15 -12
- metadata +1 -1
data/Rakefile
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'class_loader/support'
|
2
|
+
require 'monitor'
|
2
3
|
|
3
4
|
module ClassLoader
|
4
5
|
@loaded_classes = {}
|
6
|
+
@monitor = Monitor.new
|
5
7
|
class << self
|
6
|
-
attr_reader :loaded_classes
|
8
|
+
attr_reader :loaded_classes, :monitor
|
7
9
|
|
8
10
|
# Hierarchically searching for class file, according to modules hierarchy.
|
9
11
|
#
|
@@ -13,65 +15,69 @@ module ClassLoader
|
|
13
15
|
# - '/lib/a/b/c.rb' - there's nothing, moving up in hierarchy.
|
14
16
|
# - '/lib/a/c.rb' - got and load it.
|
15
17
|
def load namespace, const
|
16
|
-
|
17
|
-
|
18
|
-
|
18
|
+
monitor.synchronize do
|
19
|
+
original_namespace = namespace
|
20
|
+
namespace = nil if namespace == Object or namespace == Module
|
21
|
+
target_namespace = namespace
|
22
|
+
|
23
|
+
# Need this hack to work with anonymous classes.
|
24
|
+
namespace = eval "#{name_hack(namespace)}" if namespace
|
25
|
+
|
26
|
+
# Hierarchically searching for class name.
|
27
|
+
begin
|
28
|
+
class_name = namespace ? "#{namespace.name}::#{const}" : const.to_s
|
29
|
+
class_file_name = get_file_name class_name
|
30
|
+
|
31
|
+
# Trying to load class file, if its exist.
|
32
|
+
loaded = begin
|
33
|
+
require class_file_name
|
34
|
+
true
|
35
|
+
rescue LoadError => e
|
36
|
+
# Not the best way - hardcoding error messages, but it's the fastest way
|
37
|
+
# to check existence of file & load it.
|
38
|
+
raise e unless e.message =~ /no such file.*#{Regexp.escape(class_file_name)}/
|
39
|
+
false
|
40
|
+
end
|
19
41
|
|
20
|
-
|
21
|
-
|
42
|
+
if loaded
|
43
|
+
# Checking that class hasn't been loaded previously, sometimes it may be caused by
|
44
|
+
# weird class definition code.
|
45
|
+
if loaded_classes.include? class_name
|
46
|
+
raise_without_self NameError, \
|
47
|
+
"something wrong with '#{const}' referenced from '#{original_namespace}' scope!"
|
48
|
+
end
|
22
49
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
50
|
+
# Checking that class defined in correct namespace, not the another one.
|
51
|
+
unless namespace ? namespace.const_defined?(const, false) : Object.const_defined?(const, false)
|
52
|
+
raise_without_self NameError, \
|
53
|
+
"class name '#{class_name}' doesn't correspond to file name '#{class_file_name}'!"
|
54
|
+
end
|
27
55
|
|
28
|
-
|
29
|
-
|
30
|
-
require class_file_name
|
31
|
-
true
|
32
|
-
rescue LoadError => e
|
33
|
-
# Not the best way - hardcoding error messages, but it's the fastest way
|
34
|
-
# to check existence of file & load it.
|
35
|
-
raise e unless e.message =~ /no such file.*#{Regexp.escape(class_file_name)}/
|
36
|
-
false
|
37
|
-
end
|
56
|
+
# Getting the class itself.
|
57
|
+
klass = namespace ? namespace.const_get(const, false) : Object.const_get(const, false)
|
38
58
|
|
39
|
-
|
40
|
-
# Checking that class hasn't been loaded previously, sometimes it may be caused by
|
41
|
-
# weird class definition code.
|
42
|
-
if loaded_classes.include? class_name
|
43
|
-
raise_without_self NameError, \
|
44
|
-
"something wrong with '#{const}' referenced from '#{original_namespace}' scope!"
|
45
|
-
end
|
59
|
+
loaded_classes[class_name] = klass
|
46
60
|
|
47
|
-
|
48
|
-
unless namespace ? namespace.const_defined?(const, false) : Object.const_defined?(const, false)
|
49
|
-
raise_without_self NameError, \
|
50
|
-
"class name '#{class_name}' doesn't correspond to file name '#{class_file_name}'!"
|
61
|
+
return klass
|
51
62
|
end
|
52
63
|
|
53
|
-
#
|
54
|
-
|
55
|
-
|
56
|
-
|
64
|
+
# Moving to higher namespace.
|
65
|
+
global_also_tried = namespace == nil
|
66
|
+
namespace = Module.namespace_for namespace.name if namespace
|
67
|
+
end until global_also_tried
|
57
68
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
# Moving to higher namespace.
|
62
|
-
global_also_tried = namespace == nil
|
63
|
-
namespace = Module.namespace_for namespace.name if namespace
|
64
|
-
end until global_also_tried
|
65
|
-
|
66
|
-
return nil
|
69
|
+
return nil
|
70
|
+
end
|
67
71
|
end
|
68
72
|
|
69
73
|
# Dynamic class loading is not thread safe (known Ruby bug), to workaround it
|
70
74
|
# You can forcefully preload all Your classes in production when Your app starts.
|
71
75
|
def preload path
|
72
|
-
|
73
|
-
|
74
|
-
|
76
|
+
monitor.synchronize do
|
77
|
+
Dir.glob("#{path}/**/*.rb").each do |class_path|
|
78
|
+
class_file_name = class_path.sub("#{path}/", '').sub(/\.rb$/, '')
|
79
|
+
require class_file_name
|
80
|
+
end
|
75
81
|
end
|
76
82
|
end
|
77
83
|
|
@@ -83,7 +89,7 @@ module ClassLoader
|
|
83
89
|
|
84
90
|
def watcher
|
85
91
|
require 'class_loader/watcher'
|
86
|
-
@watcher ||= ClassLoader::Watcher.new
|
92
|
+
@watcher ||= ClassLoader::Watcher.new monitor
|
87
93
|
end
|
88
94
|
|
89
95
|
protected
|
data/lib/class_loader/watcher.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
class ClassLoader::Watcher
|
2
2
|
attr_accessor :paths, :interval
|
3
3
|
|
4
|
-
def initialize
|
4
|
+
def initialize monitor
|
5
|
+
@monitor = monitor
|
5
6
|
@paths, @files = [], {}
|
6
7
|
@interval = 2
|
7
8
|
end
|
@@ -23,22 +24,24 @@ class ClassLoader::Watcher
|
|
23
24
|
end
|
24
25
|
|
25
26
|
def check
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
if last_updated_at
|
31
|
-
|
32
|
-
|
33
|
-
|
27
|
+
monitor.synchronize do
|
28
|
+
paths.each do |path|
|
29
|
+
Dir.glob("#{path}/**/*.rb").each do |class_path|
|
30
|
+
updated_at = File.mtime class_path
|
31
|
+
if last_updated_at = files[class_path]
|
32
|
+
if last_updated_at < updated_at
|
33
|
+
class_file_name = class_path.sub "#{path}/", ''
|
34
|
+
warn "reloading #{class_file_name}"
|
35
|
+
load class_file_name
|
36
|
+
end
|
37
|
+
else
|
38
|
+
files[class_path] = updated_at
|
34
39
|
end
|
35
|
-
else
|
36
|
-
files[class_path] = updated_at
|
37
40
|
end
|
38
41
|
end
|
39
42
|
end
|
40
43
|
end
|
41
44
|
|
42
45
|
protected
|
43
|
-
attr_reader :files, :thread
|
46
|
+
attr_reader :files, :thread, :monitor
|
44
47
|
end
|