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 CHANGED
@@ -4,7 +4,7 @@ project(
4
4
  name: "class_loader",
5
5
  gem: true,
6
6
  summary: "Automatically find, load and reload classes",
7
- version: '3.0.0',
7
+ # version: '3.0.0',
8
8
 
9
9
  author: "Alexey Petrushin",
10
10
  homepage: "http://github.com/alexeypetrushin/class_loader"
@@ -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
- original_namespace = namespace
17
- namespace = nil if namespace == Object or namespace == Module
18
- target_namespace = namespace
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
- # Need this hack to work with anonymous classes.
21
- namespace = eval "#{name_hack(namespace)}" if namespace
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
- # Hierarchically searching for class name.
24
- begin
25
- class_name = namespace ? "#{namespace.name}::#{const}" : const.to_s
26
- class_file_name = get_file_name class_name
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
- # Trying to load class file, if its exist.
29
- loaded = begin
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
- if loaded
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
- # Checking that class defined in correct namespace, not the another one.
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
- # Getting the class itself.
54
- klass = namespace ? namespace.const_get(const, false) : Object.const_get(const, false)
55
-
56
- loaded_classes[class_name] = klass
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
- return klass
59
- end
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
- Dir.glob("#{path}/**/*.rb").each do |class_path|
73
- class_file_name = class_path.sub("#{path}/", '').sub(/\.rb$/, '')
74
- require class_file_name
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
@@ -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
- paths.each do |path|
27
- Dir.glob("#{path}/**/*.rb").each do |class_path|
28
- updated_at = File.mtime class_path
29
- if last_updated_at = files[class_path]
30
- if last_updated_at < updated_at
31
- class_file_name = class_path.sub "#{path}/", ''
32
- warn "reloading #{class_file_name}"
33
- load class_file_name
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: class_loader
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.0.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: