class_loader 0.4.11 → 0.4.12
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/class_loader/chained_adapter.rb +12 -6
- data/lib/class_loader/class_loader.rb +59 -52
- data/lib/class_loader/file_system_adapter/camel_case_translator.rb +2 -2
- data/lib/class_loader/file_system_adapter/underscored_translator.rb +2 -2
- data/lib/class_loader/file_system_adapter.rb +45 -39
- data/lib/class_loader/spec.rb +17 -0
- data/lib/class_loader/support.rb +1 -1
- data/lib/class_loader.rb +8 -4
- data/readme.md +17 -17
- data/spec/class_loader_spec/underscored/underscored_namespace/underscored_class.rb +1 -1
- data/spec/class_loader_spec/unload_old_class/UnloadOldClass.rb +1 -1
- data/spec/class_loader_spec.rb +40 -40
- data/spec/file_system_adapter_spec.rb +36 -36
- data/spec/translators_spec.rb +5 -5
- metadata +19 -29
@@ -4,7 +4,7 @@ class ClassLoader::ChainedAdapter
|
|
4
4
|
def initialize
|
5
5
|
@adapters = []
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
%w(
|
9
9
|
exist?
|
10
10
|
read
|
@@ -21,22 +21,28 @@ class ClassLoader::ChainedAdapter
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
24
|
-
|
25
|
-
def each_changed_class &block
|
24
|
+
|
25
|
+
def each_changed_class &block
|
26
26
|
adapters.each{|a| a.each_changed_class &block}
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
def each_class &block
|
30
30
|
adapters.each{|a| a.each_class &block}
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
def clear
|
34
34
|
adapters.each{|a| a.clear}
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
def add_path *args
|
38
38
|
adapters.each do |a|
|
39
39
|
a.add_path *args if a.respond_to? :add_path
|
40
40
|
end
|
41
41
|
end
|
42
|
+
|
43
|
+
def delete_path *args
|
44
|
+
adapters.each do |a|
|
45
|
+
a.add_path *args if a.respond_to? :delete_path
|
46
|
+
end
|
47
|
+
end
|
42
48
|
end
|
@@ -5,16 +5,16 @@ warn 'ClassLoader: working in slow, debug mode with explicit tmp file generation
|
|
5
5
|
module ClassLoader
|
6
6
|
@observers = []
|
7
7
|
SYNC = Monitor.new
|
8
|
-
|
9
|
-
class << self
|
10
|
-
def loaded_classes; @loaded_classes ||= {} end
|
11
|
-
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def loaded_classes; @loaded_classes ||= {} end
|
11
|
+
|
12
12
|
def load_class namespace, const, reload = false
|
13
13
|
SYNC.synchronize do
|
14
14
|
original_namespace = namespace
|
15
15
|
namespace = nil if namespace == Object or namespace == Module
|
16
16
|
target_namespace = namespace
|
17
|
-
|
17
|
+
|
18
18
|
# Name hack (for anonymous classes)
|
19
19
|
|
20
20
|
namespace = eval "#{name_hack(namespace)}" if namespace
|
@@ -23,24 +23,24 @@ module ClassLoader
|
|
23
23
|
simple_also_tried = false
|
24
24
|
begin
|
25
25
|
simple_also_tried = (namespace == nil)
|
26
|
-
|
27
|
-
if adapter.exist? class_name
|
26
|
+
|
27
|
+
if adapter.exist? class_name
|
28
28
|
if loaded_classes.include?(class_name) and !reload
|
29
29
|
raise_without_self NameError, "something wrong with '#{const}' referenced from '#{original_namespace}' scope!"
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
load(class_name, const)
|
33
|
-
|
34
|
-
defined_in_home_scope = namespace ? namespace.const_defined?(const) : Object.const_defined?(const)
|
35
|
-
|
33
|
+
|
34
|
+
defined_in_home_scope = namespace ? namespace.const_defined?(const) : Object.const_defined?(const)
|
35
|
+
|
36
36
|
unless defined_in_home_scope
|
37
37
|
msg = "Class Name '#{class_name}' doesn't correspond to File Name '#{adapter.to_file_path(class_name)}'!"
|
38
38
|
raise msg
|
39
39
|
# raise_without_self NameError, msg
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
result = namespace ? namespace.const_get(const) : Object.const_get(const)
|
43
|
-
|
43
|
+
|
44
44
|
loaded_classes[class_name] = target_namespace
|
45
45
|
notify_observers result
|
46
46
|
return result
|
@@ -49,25 +49,25 @@ module ClassLoader
|
|
49
49
|
class_name = namespace ? "#{namespace.name}::#{const}" : const
|
50
50
|
end
|
51
51
|
end until simple_also_tried
|
52
|
-
|
52
|
+
|
53
53
|
return false
|
54
54
|
end
|
55
55
|
end
|
56
|
-
|
57
|
-
def reload_class class_name
|
56
|
+
|
57
|
+
def reload_class class_name
|
58
58
|
SYNC.synchronize do
|
59
59
|
class_name = class_name.sub(/^::/, "")
|
60
60
|
namespace = Module.namespace_for(class_name)
|
61
|
-
name = class_name.sub(/^#{namespace}::/, "")
|
62
|
-
|
61
|
+
name = class_name.sub(/^#{namespace}::/, "")
|
62
|
+
|
63
63
|
# removing old class
|
64
64
|
# class_container = (namespace || Object)
|
65
65
|
# class_container.send :remove_const, name if class_container.const_defined? name
|
66
|
-
|
66
|
+
|
67
67
|
return load_class namespace, name, true
|
68
68
|
end
|
69
69
|
end
|
70
|
-
|
70
|
+
|
71
71
|
def wrap_inside_namespace namespace, script
|
72
72
|
nesting = []
|
73
73
|
if namespace
|
@@ -82,29 +82,36 @@ module ClassLoader
|
|
82
82
|
ending = nesting.collect{"end"}.join('; ')
|
83
83
|
return "#{begining}#{script} \n#{ending}"
|
84
84
|
end
|
85
|
-
|
86
|
-
|
87
|
-
#
|
85
|
+
|
86
|
+
|
87
|
+
#
|
88
88
|
# Utilities
|
89
|
-
#
|
90
|
-
def
|
89
|
+
#
|
90
|
+
def autoload_path path, watch = false, start_watch_thread = true
|
91
91
|
hook!
|
92
92
|
start_watching! if watch and start_watch_thread
|
93
93
|
adapter.add_path path, watch
|
94
94
|
end
|
95
|
-
|
95
|
+
def autoload_dir *a, &b
|
96
|
+
warn 'ClassLoader: the :autoload_dir method is deprecated, please use :autoload_path'
|
97
|
+
autoload_path *a, &b
|
98
|
+
end
|
99
|
+
def delete_path path
|
100
|
+
adapter.delete_path path
|
101
|
+
end
|
102
|
+
|
96
103
|
def clear
|
97
104
|
self.adapter = nil
|
98
105
|
self.observers = []
|
99
106
|
# self.error_on_defined_constant = false
|
100
107
|
end
|
101
|
-
|
102
|
-
attr_accessor :observers
|
108
|
+
|
109
|
+
attr_accessor :observers
|
103
110
|
def add_observer █ observers << block end
|
104
111
|
def notify_observers o
|
105
112
|
observers.each{|obs| obs.call o}
|
106
113
|
end
|
107
|
-
|
114
|
+
|
108
115
|
def hook!
|
109
116
|
return if @hooked
|
110
117
|
|
@@ -117,32 +124,32 @@ module ClassLoader
|
|
117
124
|
else
|
118
125
|
const_missing_without_class_loader const
|
119
126
|
end
|
120
|
-
end
|
127
|
+
end
|
121
128
|
end
|
122
129
|
@hooked = true
|
123
130
|
end
|
124
|
-
|
131
|
+
|
125
132
|
attr_writer :adapter
|
126
133
|
def adapter
|
127
134
|
@adapter ||= default_adapter
|
128
135
|
end
|
129
|
-
|
130
|
-
|
131
|
-
#
|
136
|
+
|
137
|
+
|
138
|
+
#
|
132
139
|
# Watcher thread
|
133
|
-
#
|
140
|
+
#
|
134
141
|
attr_accessor :watch_interval
|
135
142
|
def start_watching!
|
136
143
|
# reloading doesn works in debug mode, because we by ourself are generating tmp source files
|
137
144
|
return if defined?(CLASS_LOADER_GENERATE_TMP_FILES)
|
138
|
-
|
139
|
-
unless @watching_thread
|
140
|
-
@watching_thread = Thread.new do
|
145
|
+
|
146
|
+
unless @watching_thread
|
147
|
+
@watching_thread = Thread.new do
|
141
148
|
while true
|
142
149
|
sleep(watch_interval || 2)
|
143
150
|
adapter.each_changed_class do |class_name|
|
144
151
|
puts "reloading #{class_name}"
|
145
|
-
reload_class class_name
|
152
|
+
reload_class class_name
|
146
153
|
end
|
147
154
|
end
|
148
155
|
end
|
@@ -155,32 +162,32 @@ module ClassLoader
|
|
155
162
|
@watching_thread = nil
|
156
163
|
end
|
157
164
|
end
|
158
|
-
|
165
|
+
|
159
166
|
def preload!
|
160
167
|
adapter.each_class do |class_name|
|
161
168
|
reload_class class_name
|
162
169
|
end
|
163
170
|
end
|
164
|
-
|
165
|
-
|
171
|
+
|
172
|
+
|
166
173
|
protected
|
167
174
|
def default_adapter
|
168
|
-
adapter = ChainedAdapter.new
|
175
|
+
adapter = ChainedAdapter.new
|
169
176
|
adapter.adapters << FileSystemAdapter.new(UnderscoredTranslator)
|
170
|
-
adapter.adapters << FileSystemAdapter.new(CamelCaseTranslator)
|
171
|
-
adapter
|
177
|
+
adapter.adapters << FileSystemAdapter.new(CamelCaseTranslator)
|
178
|
+
adapter
|
172
179
|
end
|
173
|
-
|
174
|
-
def load class_name, const
|
180
|
+
|
181
|
+
def load class_name, const
|
175
182
|
script = adapter.read class_name
|
176
|
-
script = wrap_inside_namespace Module.namespace_for(class_name), script
|
183
|
+
script = wrap_inside_namespace Module.namespace_for(class_name), script
|
177
184
|
file_path = adapter.to_file_path(class_name)
|
178
185
|
|
179
186
|
# sometimes we need to generate file explicitly
|
180
187
|
# for example evaluated code will not be shown in Ruby coverage tool
|
181
188
|
unless defined?(CLASS_LOADER_GENERATE_TMP_FILES)
|
182
189
|
eval script, TOPLEVEL_BINDING, file_path
|
183
|
-
else
|
190
|
+
else
|
184
191
|
if file_path =~ /\.rb$/
|
185
192
|
tmp_file_path = file_path.sub /\.rb$/, '.cltmp.rb'
|
186
193
|
begin
|
@@ -194,12 +201,12 @@ module ClassLoader
|
|
194
201
|
end
|
195
202
|
end
|
196
203
|
end
|
197
|
-
|
204
|
+
|
198
205
|
def raise_without_self exception, message
|
199
206
|
raise exception, message, caller.select{|path| path !~ /\/lib\/class_loader\// and path !~ /monitor\.rb/}
|
200
207
|
end
|
201
|
-
|
202
|
-
def name_hack namespace
|
208
|
+
|
209
|
+
def name_hack namespace
|
203
210
|
if namespace
|
204
211
|
result = namespace.to_s.gsub("#<Class:", "").gsub(">", "")
|
205
212
|
result =~ /^\d/ ? "" : result
|
@@ -3,12 +3,12 @@ class ClassLoader::CamelCaseTranslator
|
|
3
3
|
raise "internall error, invalid format for #{normalized_file_name}!" if normalized_file_name =~ /^\//
|
4
4
|
normalized_file_name.gsub('/', '::')
|
5
5
|
end
|
6
|
-
|
6
|
+
|
7
7
|
def self.to_file_path class_name
|
8
8
|
raise "internall error, invalid format for #{class_name}!" if class_name =~ /^::/
|
9
9
|
class_name.gsub('::', '/')
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
def self.is_it_class? normalized_file_name
|
13
13
|
raise "internall error, invalid format for #{normalized_file_name}!" if normalized_file_name =~ /^\//
|
14
14
|
normalized_file_name[0..0] =~ /[A-Z]/
|
@@ -3,12 +3,12 @@ module ClassLoader::UnderscoredTranslator
|
|
3
3
|
raise "internall error, invalid format for #{normalized_file_name}!" if normalized_file_name =~ /^\//
|
4
4
|
normalized_file_name.camelize
|
5
5
|
end
|
6
|
-
|
6
|
+
|
7
7
|
def self.to_file_path class_name
|
8
8
|
raise "internall error, invalid format for #{class_name}!" if class_name =~ /^::/
|
9
9
|
class_name.underscore
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
def self.is_it_class? normalized_file_name
|
13
13
|
raise "internall error, invalid format for #{normalized_file_name}!" if normalized_file_name =~ /^\//
|
14
14
|
normalized_file_name[0..0] =~ /[a-z]/
|
@@ -1,28 +1,28 @@
|
|
1
1
|
class ClassLoader::FileSystemAdapter
|
2
2
|
attr_reader :translator
|
3
|
-
|
4
|
-
def initialize class_name_translator
|
3
|
+
|
4
|
+
def initialize class_name_translator
|
5
5
|
@translator = class_name_translator
|
6
6
|
@paths, @watched_paths, @file_name_cache = [], [], {}
|
7
7
|
@watched_files, @first_check = {}, true
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
def exist? class_name
|
11
11
|
!!to_file_path(class_name)
|
12
12
|
end
|
13
13
|
alias_method :exists?, :exist?
|
14
|
-
|
15
|
-
def read class_name
|
14
|
+
|
15
|
+
def read class_name
|
16
16
|
file_path = to_file_path class_name
|
17
17
|
return nil unless file_path
|
18
|
-
|
18
|
+
|
19
19
|
if file_path =~ /\.rb$/
|
20
|
-
File.open(file_path){|f| f.read}
|
20
|
+
File.open(file_path){|f| f.read}
|
21
21
|
else
|
22
22
|
"module #{class_name}; end;"
|
23
|
-
end
|
23
|
+
end
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
def to_file_path class_name
|
27
27
|
file_path, exist = @file_name_cache[class_name] || []
|
28
28
|
unless exist
|
@@ -35,7 +35,7 @@ class ClassLoader::FileSystemAdapter
|
|
35
35
|
throw :found, try
|
36
36
|
end
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
# dirs
|
40
40
|
paths.each do |base|
|
41
41
|
try = "#{base}/#{normalized_name}"
|
@@ -43,21 +43,21 @@ class ClassLoader::FileSystemAdapter
|
|
43
43
|
throw :found, try
|
44
44
|
end
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
nil
|
48
48
|
end
|
49
|
-
@file_name_cache[class_name] = [file_path, true]
|
49
|
+
@file_name_cache[class_name] = [file_path, true]
|
50
50
|
end
|
51
51
|
file_path
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
def to_class_name normalized_path
|
55
|
-
raise "Internal error, file_name should be absolute path (#{normalized_path})!" unless normalized_path =~ /^\//
|
56
|
-
raise "Internal error, file_name should be without .rb suffix (#{normalized_path})!" if normalized_path =~ /\.rb$/
|
57
|
-
|
58
|
-
paths.each do |base_path|
|
55
|
+
raise "Internal error, file_name should be absolute path (#{normalized_path})!" unless normalized_path =~ /^\//
|
56
|
+
raise "Internal error, file_name should be without .rb suffix (#{normalized_path})!" if normalized_path =~ /\.rb$/
|
57
|
+
|
58
|
+
paths.each do |base_path|
|
59
59
|
if normalized_path.start_with? base_path
|
60
|
-
normalized_name = normalized_path.sub(base_path + '/', '')
|
60
|
+
normalized_name = normalized_path.sub(base_path + '/', '')
|
61
61
|
if translator.is_it_class? normalized_name
|
62
62
|
return translator.to_class_name(normalized_name)
|
63
63
|
end
|
@@ -65,8 +65,8 @@ class ClassLoader::FileSystemAdapter
|
|
65
65
|
end
|
66
66
|
nil
|
67
67
|
end
|
68
|
-
|
69
|
-
def add_path base_path, watch = false
|
68
|
+
|
69
|
+
def add_path base_path, watch = false
|
70
70
|
base_path = File.expand_path(base_path)
|
71
71
|
# raise "#{base_path} already added!" if paths.include? base_path
|
72
72
|
unless paths.include? base_path
|
@@ -74,59 +74,65 @@ class ClassLoader::FileSystemAdapter
|
|
74
74
|
watched_paths << base_path if watch
|
75
75
|
end
|
76
76
|
end
|
77
|
-
|
77
|
+
|
78
|
+
def delete_path path
|
79
|
+
path = File.expand_path(path)
|
80
|
+
paths.delete path
|
81
|
+
watched_paths.delete path
|
82
|
+
end
|
83
|
+
|
78
84
|
def clear
|
79
85
|
@paths, @watched_paths, @file_name_cache = [], [], {}
|
80
86
|
@watched_files, @first_check = {}, true
|
81
|
-
end
|
82
|
-
|
87
|
+
end
|
88
|
+
|
83
89
|
def each_changed_class &block
|
84
90
|
unless @first_check == @watched_paths
|
85
91
|
@first_check = @watched_paths.clone
|
86
92
|
each_watched_file{|file_path, file_name| remember_file file_path}
|
87
|
-
else
|
93
|
+
else
|
88
94
|
each_watched_file do |file_path, file_name|
|
89
95
|
if file_changed? file_path
|
90
96
|
remember_file file_path
|
91
|
-
|
97
|
+
|
92
98
|
normalized_name = file_name.sub(/\.rb$/, "")
|
93
99
|
block.call translator.to_class_name(normalized_name)
|
94
100
|
end
|
95
|
-
end
|
96
|
-
end
|
101
|
+
end
|
102
|
+
end
|
97
103
|
end
|
98
|
-
|
104
|
+
|
99
105
|
def each_class &block
|
100
|
-
@paths.each do |base_path|
|
106
|
+
@paths.each do |base_path|
|
101
107
|
Dir.glob("#{base_path}/**/*.rb").each do |file_path|
|
102
108
|
normalized_path = file_path.sub(/\.rb$/, "")
|
103
|
-
|
109
|
+
|
104
110
|
normalized_name = normalized_path.sub(base_path + "/", '')
|
105
111
|
class_name = translator.to_class_name(normalized_name)
|
106
112
|
block.call class_name
|
107
113
|
end
|
108
114
|
end
|
109
115
|
end
|
110
|
-
|
116
|
+
|
111
117
|
def each_watched_file &block
|
112
|
-
@watched_paths.each do |base_path|
|
118
|
+
@watched_paths.each do |base_path|
|
113
119
|
Dir.glob("#{base_path}/**/*.rb").each do |file_path|
|
114
120
|
file_name = file_path.sub(base_path + '/', '')
|
115
121
|
|
116
|
-
if translator.is_it_class? file_name
|
122
|
+
if translator.is_it_class? file_name
|
117
123
|
block.call file_path, file_name
|
118
124
|
end
|
119
125
|
end
|
120
126
|
end
|
121
127
|
end
|
122
|
-
|
128
|
+
|
123
129
|
def inspect
|
124
130
|
"FileSystemAdapter (#{@paths.join(', ')})"
|
125
|
-
end
|
126
|
-
|
131
|
+
end
|
132
|
+
|
127
133
|
protected
|
128
|
-
attr_reader :paths, :watched_paths, :watcher, :watched_files
|
129
|
-
|
134
|
+
attr_reader :paths, :watched_paths, :watcher, :watched_files
|
135
|
+
|
130
136
|
def file_changed? file_path
|
131
137
|
old_time = watched_files[file_path]
|
132
138
|
old_time == nil or old_time != File.mtime(file_path)
|
@@ -135,5 +141,5 @@ class ClassLoader::FileSystemAdapter
|
|
135
141
|
def remember_file file_path
|
136
142
|
watched_files[file_path] = File.mtime(file_path)
|
137
143
|
end
|
138
|
-
|
144
|
+
|
139
145
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rspec_ext'
|
2
|
+
|
3
|
+
rspec do
|
4
|
+
def self.with_autoload_path *paths
|
5
|
+
before(:all){paths.each{|path| ClassLoader.autoload_path path}}
|
6
|
+
after(:all){paths.each{|path| ClassLoader.delete_path path}}
|
7
|
+
end
|
8
|
+
|
9
|
+
def with_autoload_path *paths, &b
|
10
|
+
begin
|
11
|
+
paths.each{|path| ClassLoader.autoload_path path}
|
12
|
+
b.call
|
13
|
+
ensure
|
14
|
+
paths.each{|path| ClassLoader.delete_path path}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/class_loader/support.rb
CHANGED
data/lib/class_loader.rb
CHANGED
@@ -7,11 +7,15 @@ end
|
|
7
7
|
support
|
8
8
|
file_system_adapter/camel_case_translator
|
9
9
|
file_system_adapter/underscored_translator
|
10
|
-
file_system_adapter
|
10
|
+
file_system_adapter
|
11
11
|
chained_adapter
|
12
12
|
class_loader
|
13
13
|
).each{|f| require "class_loader/#{f}"}
|
14
14
|
|
15
|
-
def
|
16
|
-
ClassLoader.
|
17
|
-
end
|
15
|
+
def autoload_path *args, &block
|
16
|
+
ClassLoader.autoload_path *args, &block
|
17
|
+
end
|
18
|
+
def autoload_dir *a, &b
|
19
|
+
warn 'ClassLoader: the :autoload_dir method is deprecated, please use :autoload_path'
|
20
|
+
autoload_path *a, &b
|
21
|
+
end
|
data/readme.md
CHANGED
@@ -1,45 +1,45 @@
|
|
1
1
|
# Automatically finds and loads classes for Your Ruby App
|
2
2
|
|
3
3
|
## Overview
|
4
|
-
There's only one method - :
|
4
|
+
There's only one method - :autoload_path, kind of turbocharged :autoload, it understands namespaces, figure out dependencies and can watch and reload changed files.
|
5
5
|
|
6
|
-
Let's say
|
6
|
+
Let's say Your application has the following structure
|
7
7
|
|
8
8
|
/your_app
|
9
|
-
/lib
|
9
|
+
/lib
|
10
10
|
/animals
|
11
11
|
/dog.rb
|
12
12
|
/zoo.rb
|
13
13
|
|
14
|
-
Just point ClassLoader to the directory(ies)
|
14
|
+
Just point ClassLoader to the directory(ies) Your classes are located and it will find and load them automatically
|
15
15
|
|
16
16
|
require 'class_loader'
|
17
|
-
|
18
|
-
|
17
|
+
autoload_path '/your_app/lib'
|
18
|
+
|
19
19
|
Zoo.add Animals::Dog.new # <= all classes loaded automatically
|
20
|
-
|
20
|
+
|
21
21
|
no need for
|
22
22
|
|
23
23
|
# require 'animals/dog'
|
24
24
|
# require 'app'
|
25
|
-
|
25
|
+
|
26
26
|
you can specify multiple autoload directories, and tell it to watch them
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
autoload_path '/your_app/lib', true # <= provide true as the second argument
|
29
|
+
autoload_path '/your_app/another_lib'
|
30
|
+
|
31
31
|
**Note**: In the dog.rb we write just the "class Dog; end", instead of "module Animals; class Dog; end; end', and there are no really the 'Animals' module, ClassLoader smart enough to figure it out that there's should be one by looking at files structure and it will generate it on the fly.
|
32
32
|
|
33
|
-
Also you can use CamelCase notation or provide
|
33
|
+
Also you can use CamelCase notation or provide Your own class_name/file_path translator, or even provide Your own custom resource adapter that for example will look for classes on the net and download them.
|
34
34
|
|
35
|
-
There's currently a known bug in Ruby 1.8.x - class loading isn't thread safe, so in production you should preload all
|
35
|
+
There's currently a known bug in Ruby 1.8.x - class loading isn't thread safe, so in production you should preload all Your classes
|
36
36
|
|
37
37
|
ClassLoader.preload! if app_in_production?
|
38
38
|
|
39
39
|
## Installation
|
40
40
|
|
41
41
|
$ gem install class_loader
|
42
|
-
|
43
|
-
##
|
44
|
-
|
45
|
-
Copyright (c) Alexey Petrushin http://
|
42
|
+
|
43
|
+
## License
|
44
|
+
|
45
|
+
Copyright (c) Alexey Petrushin http://petrush.in, released under the MIT license.
|
@@ -1,2 +1,2 @@
|
|
1
1
|
class UnderscoredClass
|
2
|
-
end
|
2
|
+
end
|
@@ -1,2 +1,2 @@
|
|
1
|
-
class UnloadOldClass
|
1
|
+
class UnloadOldClass
|
2
2
|
end
|
data/spec/class_loader_spec.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'rspec_ext'
|
2
2
|
require "class_loader"
|
3
3
|
|
4
|
-
describe ClassLoader do
|
4
|
+
describe ClassLoader do
|
5
5
|
with_tmp_spec_dir
|
6
|
-
|
6
|
+
|
7
7
|
after :all do
|
8
8
|
remove_constants %w(
|
9
9
|
BasicSpec
|
@@ -18,51 +18,51 @@ describe ClassLoader do
|
|
18
18
|
UnderscoredNamespace
|
19
19
|
)
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
after do
|
23
23
|
ClassLoader.clear
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
it "should load classes from class path" do
|
27
|
-
|
28
|
-
|
27
|
+
autoload_path "#{spec_dir}/basic"
|
28
|
+
|
29
29
|
BasicSpec
|
30
30
|
BasicSpec::SomeNamespace::SomeClass
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
it "should load classes only once" do
|
34
|
-
|
35
|
-
|
34
|
+
autoload_path "#{spec_dir}/only_once"
|
35
|
+
|
36
36
|
check = mock
|
37
37
|
check.should_receive(:loaded).once
|
38
38
|
ClassLoader.add_observer do |klass|
|
39
39
|
klass.name.should == "OnlyOnceSpec"
|
40
40
|
check.loaded
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
OnlyOnceSpec
|
44
44
|
OnlyOnceSpec
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
it "should resolve is namespace a class or module" do
|
48
|
-
|
49
|
-
|
48
|
+
autoload_path "#{spec_dir}/namespace_type_resolving"
|
49
|
+
|
50
50
|
NamespaceTypeResolving.class.should == Class
|
51
51
|
NamespaceTypeResolving::SomeClass
|
52
|
-
|
53
|
-
class NamespaceIsAlreadyDefinedAsClass; end
|
52
|
+
|
53
|
+
class NamespaceIsAlreadyDefinedAsClass; end
|
54
54
|
NamespaceIsAlreadyDefinedAsClass::SomeClass
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
it "should recognize infinity loop" do
|
58
|
-
|
59
|
-
|
58
|
+
autoload_path "#{spec_dir}/infinity_loop"
|
59
|
+
|
60
60
|
-> {InfinityLoop}.should raise_error(/Class Name .+ doesn't correspond to File Name/)
|
61
61
|
end
|
62
|
-
|
62
|
+
|
63
63
|
it "should correctly works inside of anonymous class" do
|
64
|
-
|
65
|
-
|
64
|
+
autoload_path "#{spec_dir}/anonymous_class"
|
65
|
+
|
66
66
|
module ::AnonymousSpec
|
67
67
|
class << self
|
68
68
|
def anonymous
|
@@ -70,13 +70,13 @@ describe ClassLoader do
|
|
70
70
|
end
|
71
71
|
end
|
72
72
|
end
|
73
|
-
|
73
|
+
|
74
74
|
AnonymousSpec.anonymous
|
75
75
|
end
|
76
|
-
|
76
|
+
|
77
77
|
it "should raise exception if class defined in another namespace" do
|
78
|
-
|
79
|
-
|
78
|
+
autoload_path "#{spec_dir}/another_namespace"
|
79
|
+
|
80
80
|
AnotherNamespace::NamespaceA
|
81
81
|
# ClassLoader.error_on_defined_constant = true
|
82
82
|
-> {
|
@@ -84,21 +84,21 @@ describe ClassLoader do
|
|
84
84
|
}.should raise_error(/something wrong with/)
|
85
85
|
# }.should raise_error(/Class '.+' is not defined in the '.+' Namespace!/)
|
86
86
|
end
|
87
|
-
|
88
|
-
describe "reloading" do
|
87
|
+
|
88
|
+
describe "reloading" do
|
89
89
|
it "should reload class files" do
|
90
90
|
class_reloading_dir = "#{spec_dir}/class_reloading"
|
91
91
|
fname = "#{class_reloading_dir}/ClassReloadingSpec.rb"
|
92
|
-
|
92
|
+
autoload_path class_reloading_dir
|
93
93
|
|
94
94
|
code = <<-RUBY
|
95
95
|
class ClassReloadingSpec
|
96
96
|
def self.check; :value end
|
97
97
|
end
|
98
98
|
RUBY
|
99
|
-
|
99
|
+
|
100
100
|
File.open(fname, 'w'){|f| f.write code}
|
101
|
-
|
101
|
+
|
102
102
|
ClassReloadingSpec.check.should == :value
|
103
103
|
|
104
104
|
code = <<-RUBY
|
@@ -108,30 +108,30 @@ end
|
|
108
108
|
RUBY
|
109
109
|
|
110
110
|
File.open(fname, 'w'){|f| f.write code}
|
111
|
-
|
111
|
+
|
112
112
|
ClassLoader.reload_class(ClassReloadingSpec.name)
|
113
113
|
ClassReloadingSpec.check.should == :another_value
|
114
114
|
end
|
115
115
|
|
116
|
-
# Outdated
|
116
|
+
# Outdated
|
117
117
|
# it "should unload old classes before reloading" do
|
118
|
-
#
|
118
|
+
# autoload_path "#{spec_dir}/unload_old_class"
|
119
119
|
# UnloadOldClass.instance_variable_set "@value", :value
|
120
120
|
# ClassLoader.reload_class(UnloadOldClass.name)
|
121
121
|
# UnloadOldClass.instance_variable_get("@value").should == nil
|
122
122
|
# end
|
123
123
|
end
|
124
|
-
|
124
|
+
|
125
125
|
it "should be able to preload all classes in production" do
|
126
|
-
|
127
|
-
Object.const_defined?(:PreloadingSpec).should be_false
|
126
|
+
autoload_path "#{spec_dir}/preloading"
|
127
|
+
Object.const_defined?(:PreloadingSpec).should be_false
|
128
128
|
ClassLoader.preload!
|
129
129
|
Object.const_defined?(:PreloadingSpec).should be_true
|
130
|
-
end
|
131
|
-
|
130
|
+
end
|
131
|
+
|
132
132
|
it "underscored smoke test" do
|
133
|
-
|
134
|
-
|
133
|
+
autoload_path "#{spec_dir}/underscored"
|
134
|
+
|
135
135
|
UnderscoredNamespace::UnderscoredClass
|
136
136
|
end
|
137
137
|
end
|
@@ -1,109 +1,109 @@
|
|
1
1
|
require "rspec_ext"
|
2
2
|
require "class_loader"
|
3
3
|
|
4
|
-
describe ClassLoader::FileSystemAdapter do
|
4
|
+
describe ClassLoader::FileSystemAdapter do
|
5
5
|
with_tmp_spec_dir
|
6
|
-
|
6
|
+
|
7
7
|
before do
|
8
|
-
@fs_adapter = ClassLoader::FileSystemAdapter.new(ClassLoader::CamelCaseTranslator)
|
9
|
-
|
8
|
+
@fs_adapter = ClassLoader::FileSystemAdapter.new(ClassLoader::CamelCaseTranslator)
|
9
|
+
|
10
10
|
# Actually we are testing both ChainedAdapter and FileSystemAdapter
|
11
11
|
@adapter = ClassLoader::ChainedAdapter.new
|
12
12
|
@adapter.adapters << @fs_adapter
|
13
|
-
|
14
|
-
@adapter.add_path "#{spec_dir}/common"
|
13
|
+
|
14
|
+
@adapter.add_path "#{spec_dir}/common"
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def write_file path, klass
|
18
18
|
File.open("#{spec_dir}/#{path}", 'w'){|f| f.write "class #{klass}; end"}
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
it "exist?" do
|
22
22
|
@adapter.exist?("SomeNamespace").should be_true
|
23
|
-
@adapter.exist?("SomeNamespace::SomeClass").should be_true
|
23
|
+
@adapter.exist?("SomeNamespace::SomeClass").should be_true
|
24
24
|
@adapter.exist?("SomeNamespace::NonExistingClass").should be_false
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
it "should works with multiple class paths" do
|
28
28
|
@adapter.add_path "#{spec_dir}/multiple_class_paths/path_a"
|
29
29
|
@adapter.add_path "#{spec_dir}/multiple_class_paths/path_b"
|
30
|
-
|
30
|
+
|
31
31
|
@adapter.exist?("ClassInPathA").should be_true
|
32
32
|
@adapter.exist?("ClassInPathB").should be_true
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
it "read" do
|
36
|
-
@adapter.read("SomeNamespace::SomeClass").should == "class SomeClass; end"
|
36
|
+
@adapter.read("SomeNamespace::SomeClass").should == "class SomeClass; end"
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
it "to_file_path" do
|
40
40
|
@adapter.to_file_path("NonExistingClass").should be_nil
|
41
41
|
@adapter.to_file_path("SomeNamespace::SomeClass").should =~ /SomeNamespace\/SomeClass/
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
it "to_class_name" do
|
45
45
|
@adapter.to_class_name("#{spec_dir}/non_existing_path").should be_nil
|
46
46
|
@adapter.to_class_name("#{spec_dir}/common/SomeNamespace").should == "SomeNamespace"
|
47
47
|
@adapter.to_class_name("#{spec_dir}/common/SomeNamespace/SomeClass").should == "SomeNamespace::SomeClass"
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
it "shouldn't add path twice" do
|
51
51
|
@adapter.clear
|
52
52
|
@adapter.add_path "#{spec_dir}/common"
|
53
53
|
@adapter.add_path "#{spec_dir}/common"
|
54
54
|
@fs_adapter.instance_variable_get("@paths").size.should == 1
|
55
55
|
end
|
56
|
-
|
57
|
-
describe "file watching" do
|
56
|
+
|
57
|
+
describe "file watching" do
|
58
58
|
def changed_classes
|
59
59
|
changed = []
|
60
60
|
@adapter.each_changed_class{|c| changed << c}
|
61
61
|
changed
|
62
62
|
end
|
63
|
-
|
63
|
+
|
64
64
|
it "each_changed_class shouldn't affect paths not specified for watching" do
|
65
65
|
@adapter.add_path "#{spec_dir}/search_only_watched", false
|
66
|
-
changed_classes.should == []
|
67
|
-
|
66
|
+
changed_classes.should == []
|
67
|
+
|
68
68
|
sleep(1) && write_file("watching/SomeClass.rb", "SomeClass")
|
69
69
|
changed_classes.should == []
|
70
70
|
end
|
71
|
-
|
71
|
+
|
72
72
|
it "each_changed_class" do
|
73
73
|
@adapter.add_path "#{spec_dir}/watching", true
|
74
|
-
|
75
|
-
changed_classes.should == []
|
76
|
-
|
77
|
-
sleep(1) && write_file("watching/SomeClass.rb", "SomeClass")
|
74
|
+
|
75
|
+
changed_classes.should == []
|
76
|
+
|
77
|
+
sleep(1) && write_file("watching/SomeClass.rb", "SomeClass")
|
78
78
|
changed_classes.should == ["SomeClass"]
|
79
|
-
|
79
|
+
|
80
80
|
sleep(1) && write_file("watching/SomeClass.rb", "SomeClass")
|
81
81
|
changed_classes.should == ["SomeClass"]
|
82
82
|
end
|
83
83
|
end
|
84
|
-
|
84
|
+
|
85
85
|
describe "Underscored shouldn't mess with CamelCase" do
|
86
86
|
before do
|
87
|
-
@camel_case_adapter = ClassLoader::FileSystemAdapter.new(ClassLoader::CamelCaseTranslator)
|
87
|
+
@camel_case_adapter = ClassLoader::FileSystemAdapter.new(ClassLoader::CamelCaseTranslator)
|
88
88
|
@camel_case_adapter.add_path "#{spec_dir}/shouldnt_mess", true
|
89
89
|
@camel_case_file_path = "#{spec_dir}/shouldnt_mess/CamelCaseClass.rb"
|
90
|
-
|
91
|
-
@underscored_adapter = ClassLoader::FileSystemAdapter.new(ClassLoader::UnderscoredTranslator)
|
90
|
+
|
91
|
+
@underscored_adapter = ClassLoader::FileSystemAdapter.new(ClassLoader::UnderscoredTranslator)
|
92
92
|
@underscored_adapter.add_path "#{spec_dir}/shouldnt_mess", true
|
93
93
|
@underscored_file_path = "#{spec_dir}/shouldnt_mess/underscored_class.rb"
|
94
94
|
end
|
95
|
-
|
96
|
-
|
97
|
-
it "should watch only files understable by it's translator (CamelCase shouldn't load Underscored)" do
|
95
|
+
|
96
|
+
|
97
|
+
it "should watch only files understable by it's translator (CamelCase shouldn't load Underscored)" do
|
98
98
|
watched = []
|
99
99
|
@camel_case_adapter.each_watched_file{|file_path, relative_name| watched << relative_name}
|
100
100
|
watched.should == ["CamelCaseClass.rb"]
|
101
|
-
|
101
|
+
|
102
102
|
watched = []
|
103
103
|
@underscored_adapter.each_watched_file{|file_path, relative_name| watched << relative_name}
|
104
104
|
watched.should == ["underscored_class.rb"]
|
105
105
|
end
|
106
|
-
|
106
|
+
|
107
107
|
it "CamelCase to_class_name shouldn't translate Underscored" do
|
108
108
|
@camel_case_adapter.to_class_name(@camel_case_file_path.sub(/\.rb$/, '')).should == "CamelCaseClass"
|
109
109
|
@underscored_adapter.to_class_name(@underscored_file_path.sub(/\.rb$/, '')).should == "UnderscoredClass"
|
data/spec/translators_spec.rb
CHANGED
@@ -7,26 +7,26 @@ describe "Translators" do
|
|
7
7
|
before :all do
|
8
8
|
@t = ClassLoader::CamelCaseTranslator
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
it "is_it_class?" do
|
12
12
|
@t.is_it_class?("SomeClass.rb").should be_true
|
13
13
|
@t.is_it_class?("SomeNamespace/SomeClass.rb").should be_true
|
14
|
-
|
14
|
+
|
15
15
|
@t.is_it_class?("someclass.rb").should be_false
|
16
16
|
@t.is_it_class?("some_class.rb").should be_false
|
17
17
|
@t.is_it_class?("some_namespace/some_class.rb").should be_false
|
18
18
|
end
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
describe "Underscored" do
|
22
22
|
before :all do
|
23
23
|
@t = ClassLoader::UnderscoredTranslator
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
it "is_it_class?" do
|
27
27
|
@t.is_it_class?("SomeClass.rb").should be_false
|
28
28
|
@t.is_it_class?("SomeNamespace/SomeClass.rb").should be_false
|
29
|
-
|
29
|
+
|
30
30
|
@t.is_it_class?("someclass.rb").should be_true
|
31
31
|
@t.is_it_class?("some_class.rb").should be_true
|
32
32
|
@t.is_it_class?("some_namespace/some_class.rb").should be_true
|
metadata
CHANGED
@@ -1,28 +1,22 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: class_loader
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.12
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- Alexey Petrushin
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
|
13
|
-
date: 2011-08-08 00:00:00 +04:00
|
14
|
-
default_executable:
|
12
|
+
date: 2011-08-15 00:00:00.000000000Z
|
15
13
|
dependencies: []
|
16
|
-
|
17
14
|
description:
|
18
15
|
email:
|
19
16
|
executables: []
|
20
|
-
|
21
17
|
extensions: []
|
22
|
-
|
23
18
|
extra_rdoc_files: []
|
24
|
-
|
25
|
-
files:
|
19
|
+
files:
|
26
20
|
- Rakefile
|
27
21
|
- readme.md
|
28
22
|
- lib/class_loader/chained_adapter.rb
|
@@ -30,6 +24,7 @@ files:
|
|
30
24
|
- lib/class_loader/file_system_adapter/camel_case_translator.rb
|
31
25
|
- lib/class_loader/file_system_adapter/underscored_translator.rb
|
32
26
|
- lib/class_loader/file_system_adapter.rb
|
27
|
+
- lib/class_loader/spec.rb
|
33
28
|
- lib/class_loader/support.rb
|
34
29
|
- lib/class_loader/tasks.rb
|
35
30
|
- lib/class_loader.rb
|
@@ -54,33 +49,28 @@ files:
|
|
54
49
|
- spec/file_system_adapter_spec/shouldnt_mess/underscored_class.rb
|
55
50
|
- spec/file_system_adapter_spec.rb
|
56
51
|
- spec/translators_spec.rb
|
57
|
-
has_rdoc: true
|
58
52
|
homepage: http://github.com/alexeypetrushin/class_loader
|
59
53
|
licenses: []
|
60
|
-
|
61
54
|
post_install_message:
|
62
55
|
rdoc_options: []
|
63
|
-
|
64
|
-
require_paths:
|
56
|
+
require_paths:
|
65
57
|
- lib
|
66
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
59
|
none: false
|
68
|
-
requirements:
|
69
|
-
- -
|
70
|
-
- !ruby/object:Gem::Version
|
71
|
-
version:
|
72
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ! '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
65
|
none: false
|
74
|
-
requirements:
|
75
|
-
- -
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
version:
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
78
70
|
requirements: []
|
79
|
-
|
80
71
|
rubyforge_project:
|
81
|
-
rubygems_version: 1.
|
72
|
+
rubygems_version: 1.8.6
|
82
73
|
signing_key:
|
83
74
|
specification_version: 3
|
84
75
|
summary: Automatically finds, loads and reloads classes
|
85
76
|
test_files: []
|
86
|
-
|