class_loader 3.0.0 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|