Module: Autocode

Defined in:
lib/autocode.rb

Class Method Summary collapse

Class Method Details

.extended(mod) ⇒ Object

:nodoc:



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/autocode.rb', line 5

def self.extended( mod ) #:nodoc:

old = mod.method( :const_missing )
mod.metaclass.class_eval do
  
  def autocreate( key, exemplar, &block )
	  keys = case key
    when true, Symbol then [key]
	  when Array then key
	  end
	  
	  keys.each do |k|
	   	exemplars[k] = exemplar
        init_blocks[k] << block
	  end

		return self
	end
	
	def autoinit( key, &block )
		  # See whether we're dealing with a namespaced constant,
		  # The match groupings for,  e.g. "X::Y::Z", would be
		  # ["X::Y", "Z"]
		  match = key.to_s.match(/^(.*)::([\w\d_]+)$/)
		  
		  if match
		    namespace, cname = match[1,2]
		    const = module_eval(namespace)
  			const.module_eval do
  			  @init_blocks ||= Hash.new { |h,k| h[k] = [] }
  			  @init_blocks[cname.to_sym] << block
  			end
			else
			  @init_blocks ||= Hash.new { |h,k| h[k] = [] }
  			@init_blocks[key] << block
			end
			return self
		end
  
  def autoload( key, options )
    # look for load_files in either a specified directory, or in the directory
    # with the snakecase name of the enclosing module
    directories = [options[:directories] || default_directory(self.name)].flatten
      # create a lambda that looks for a file to load
      file_finder = lambda do |cname|
        filename = default_file_name(cname)
        dirname = directories.find do |dir|
			    File.exist?(File.join(dir.to_s, filename))
			  end
			  dirname ? File.join(dirname.to_s, filename) : nil
      end
      # if no exemplar is given, assume Module.new
      load_files[key] = [file_finder, options[:exemplar] || Module.new]
		return self
  end
  
  def autoload_class(key, superclass=nil, options={})
    options[:exemplar] = Class.new(superclass)
    autoload key, options
  end
  
  
  define_method :const_missing do | cname | #:nodoc:
      cname = cname.to_sym
      exemplar = exemplars[cname] || exemplars[true]
      blocks = init_blocks[cname]
      blocks = init_blocks[true] + blocks if exemplars[cname].nil? && init_blocks[true]
      load_file_finder, load_class = load_files[cname] || load_files[true]
      
	  if load_file_finder && filename = load_file_finder.call(cname)
	    object = load_class.clone
		elsif exemplar 
			object = exemplar.clone
    else
      return old.call(cname)
     end
	  			    
		(@reloadable ||= []) << cname; 
		const_set( cname, object )
       
      blocks.each do |block|
        object.module_eval( &block) if block
      end
      load(filename) if filename
		return object
	end
	
	# helper methods.  May need to make them  private.
	def exemplars
	  @exemplars ||= Hash.new
	end
	
	def init_blocks
	 @init_blocks ||= Hash.new { |h,k| h[k] = [] }
	end
  
  def load_files
	  @load_files ||= Hash.new
	end
	
	def reloadable
	 @reloadable ||= []
	end
	
	def default_file_name(cname)
	 ( cname.to_s.gsub(/([a-z\d])([A-Z\d])/){ "#{$1}_#{$2}"} << ".rb" ).downcase
	end
	
	def default_directory(module_name)
	  m = self.name.match( /^.*::([\w\d_]+)$/)
	  m[1].snake_case
	end
  
 end
end