Class: Tap::Env

Inherits:
Object show all
Includes:
Configurable, Enumerable, Support::Minimap
Defined in:
lib/tap/env.rb

Overview

– Note that gems and env_paths reset envs – custom modifications to envs will be lost whenever these configs are reset.

Direct Known Subclasses

Exe

Defined Under Namespace

Classes: ConfigError

Constant Summary collapse

DEFAULT_CONFIG_FILE =

The default config file path

"tap.yml"
TEMPLATES =
{}
@@instance =
nil
@@instances =
{}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Support::Minimap

#minimap, #minimatch

Constructor Details

#initialize(config = {}, root = Tap::Root.new, logger = nil) ⇒ Env

Returns a new instance of Env.



214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/tap/env.rb', line 214

def initialize(config={}, root=Tap::Root.new, logger=nil)
  @root = root 
  @logger = logger
  @envs = []
  @active = false
  @manifests = {}

  # initialize these for reset_env
  @gems = []
  @env_paths = []
  
  initialize_config(config)
end

Instance Attribute Details

#loggerObject

Gets or sets the logger for self



124
125
126
# File 'lib/tap/env.rb', line 124

def logger
  @logger
end

#rootObject (readonly)

The Root directory structure for self.



121
122
123
# File 'lib/tap/env.rb', line 121

def root
  @root
end

Class Method Details

.gemspecs(latest = true) ⇒ Object

Returns the gemspecs for all installed gems with a DEFAULT_CONFIG_FILE. If latest==true, then only the specs for the most current gems will be returned.



83
84
85
86
87
# File 'lib/tap/env.rb', line 83

def gemspecs(latest=true)
  Support::Gems.select_gems(latest) do |spec|
    File.exists?(File.join(spec.full_gem_path, DEFAULT_CONFIG_FILE))
  end
end

.instanceObject

Returns the active instance of Env.



17
18
19
# File 'lib/tap/env.rb', line 17

def instance
  @@instance
end

.instance_for(path) ⇒ Object



61
62
63
64
# File 'lib/tap/env.rb', line 61

def instance_for(path)
  path = pathify(path)
  instances.has_key?(path) ? instances[path] : instantiate(path)
end

.instancesObject

A hash of (path, Env instance) pairs, generated by Env#instantiate. Used to prevent infinite loops of Env dependencies by assigning a single Env to a given path.



24
25
26
# File 'lib/tap/env.rb', line 24

def instances
  @@instances
end

.instantiate(path_or_root, default_config = {}, logger = nil, &block) ⇒ Object

Creates a new Env for the specified path and adds it to Env#instances, or returns the existing instance for the path. Paths can point to an env config file, or to a directory. If a directory is provided, instantiate treats path as the DEFAULT_CONFIG_FILE in that directory. All paths are expanded.

e1 = Env.instantiate("./path/to/config.yml")
e2 = Env.instantiate("./path/to/dir")

Env.instances       
# => {
#  File.expand_path("./path/to/config.yml") => e1, 
#  File.expand_path("./path/to/dir/#{Tap::Env::DEFAULT_CONFIG_FILE}") => e2 }

The Env is initialized using configurations read from the env config file, and a Root initialized to the config file directory. An instance will be initialized regardless of whether the config file or directory exists.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/tap/env.rb', line 45

def instantiate(path_or_root, default_config={}, logger=nil, &block)
  path = path_or_root.kind_of?(Root) ? path_or_root.root : path_or_root
  path = pathify(path)
  
  begin
    root = path_or_root.kind_of?(Root) ? path_or_root : Root.new(File.dirname(path))
    config = default_config.merge(load_file(path))
    
    # note the assignment of env to instances MUST occur before
    # reconfigure to prevent infinite looping
    (instances[path] = new({}, root, logger)).reconfigure(config, &block)
  rescue(Exception)
    raise Env::ConfigError.new($!, path)
  end
end

.manifest(name, &block) ⇒ Object

:yields: env (returns manifest)



73
74
75
76
77
78
# File 'lib/tap/env.rb', line 73

def manifest(name, &block) # :yields: env (returns manifest)
  name = name.to_sym
  define_method(name) do
    self.manifests[name] ||= block.call(self).bind(self, name)
  end
end

.pathify(path) ⇒ Object



66
67
68
69
70
71
# File 'lib/tap/env.rb', line 66

def pathify(path)
  if File.directory?(path) || (!File.exists?(path) && File.extname(path) == "")
    path = File.join(path, DEFAULT_CONFIG_FILE) 
  end
  File.expand_path(path)
end

Instance Method Details

#activateObject

Activates self by unshifting load_paths for self to the load_path_targets. Once active, self can be referenced from Env.instance and the current configurations are frozen. Env.instance is deactivated, if set, before self is activated. Returns true if activate succeeded, or false if self is already active.



377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# File 'lib/tap/env.rb', line 377

def activate
  return false if active?
  
  @active = true
  @@instance = self if @@instance == nil
  
  # freeze array configs like load_paths
  config.each_pair do |key, value|
    next unless value.kind_of?(Array)
    value.freeze
  end
  
  # activate nested envs
  envs.reverse_each do |env|
    env.activate
  end

  # add load paths
  load_paths.reverse_each do |path|
    $LOAD_PATH.unshift(path)
  end

  $LOAD_PATH.uniq!
  
  # perform requires
  requires.each do |path|
    require path
  end
  
  # perform loads
  loads.each do |path|
    load path
  end
  
  true
end

#active?Boolean

Return true if self has been activated.

Returns:

  • (Boolean)


446
447
448
# File 'lib/tap/env.rb', line 446

def active?
  @active
end

#countObject

Returns the total number of unique envs nested in self (including self).



304
305
306
# File 'lib/tap/env.rb', line 304

def count
  envs(true).length
end

#deactivateObject

Deactivates self by clearing manifests and deleting load_paths for self from the load_path_targets. Env.instance will no longer reference self and the configurations are unfrozen (using duplication).

Returns true if deactivate succeeded, or false if self is not active.



419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
# File 'lib/tap/env.rb', line 419

def deactivate
  return false unless active?
  
  # remove load paths
  load_paths.each do |path|
    $LOAD_PATH.delete(path)
  end

  @active = false
  @manifests.clear
  @@instance = nil if @@instance == self
  
  # unfreeze array configs by duplicating
  config.each_pair do |key, value|
    next unless value.kind_of?(Array)
    instance_variable_set("@#{key}", value.dup)
  end
  
  # dectivate nested envs
  envs.reverse_each do |env|
    env.deactivate
  end
  
  true
end

#eachObject

Passes each nested env to the block in order, starting with self.



265
266
267
# File 'lib/tap/env.rb', line 265

def each
  envs(true).each {|e| yield(e) }
end

#env_pathObject

Returns the path for self in Env.instances.



361
362
363
364
# File 'lib/tap/env.rb', line 361

def env_path
  Env.instances.each_pair {|path, env| return path if env == self }
  nil
end

#envs(flat = false) ⇒ Object

An array of nested Envs, by default comprised of the env_path + gem environments (in that order). These nested Envs are activated/deactivated with self.

Returns a flattened array of the unique nested envs when flat == true.



241
242
243
# File 'lib/tap/env.rb', line 241

def envs(flat=false)
  flat ? (@flat_envs ||= self.flatten_envs.freeze) : @envs
end

#envs=(envs) ⇒ Object

Sets envs removing duplicates and instances of self.



229
230
231
232
233
# File 'lib/tap/env.rb', line 229

def envs=(envs)
  @envs = envs.uniq.delete_if {|e| e == self }
  @envs.freeze
  @flat_envs = nil
end

#inspect(template = nil) ⇒ Object

:yields: templater, attrs



532
533
534
535
536
537
538
539
540
541
542
# File 'lib/tap/env.rb', line 532

def inspect(template=nil) # :yields: templater, attrs
  return "#<#{self.class}:#{object_id} root='#{root.root}'>" if template == nil
  
  attrs = {}
  collect do |env|
    templater = Support::Templater.new(template, :env => env)
    block_given? ? (yield(templater, attrs) ? templater : nil) : templater
  end.compact.collect do |templater|
    templater.build(attrs)
  end.join
end

#log(action, msg = "", level = Logger::INFO) ⇒ Object

Logs the action and message at the input level (default INFO). Logging is suppressed if no logger is set.



368
369
370
# File 'lib/tap/env.rb', line 368

def log(action, msg="", level=Logger::INFO)
  logger.add(level, msg, action.to_s) if logger
end

#push(env) ⇒ Object

Pushes env onto envs, removing duplicates.

Self cannot be pushed onto self.



256
257
258
259
260
261
262
# File 'lib/tap/env.rb', line 256

def push(env)
  unless env == self || envs[-1] == env
    envs = self.envs.reject {|e| e == env }
    self.envs = envs.push(env)
  end
  self
end

#reconfigure(overrides = {}) ⇒ Object

Processes and resets the input configurations for both root and self. Reconfiguration consists of the following steps:

  • partition overrides into env, root, and other configs

  • reconfigure root with the root configs

  • reconfigure self with the env configs

  • yield other configs to the block (if given)

Reconfigure will always yields to the block, even if there are no non-root, non-env configurations. Unspecified configurations are NOT reconfigured. (Note this means that existing path configurations like load_paths will not automatically be reset using reconfigured root.)



321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
# File 'lib/tap/env.rb', line 321

def reconfigure(overrides={})
  check_configurable
  
  # partiton config into its parts
  env_configs = {}
  root_configs = {}
  other_configs = {}
  
  env_configurations = self.class.configurations
  root_configurations = root.class.configurations
  overrides.each_pair do |key, value|
    key = key.to_sym

    partition = case 
    when env_configurations.key?(key) then env_configs
    when root_configurations.key?(key) then root_configs
    else other_configs
    end

    partition[key] = value
  end

  # reconfigure root so it can resolve path_configs
  root.reconfigure(root_configs)
  
  # reconfigure self
  super(env_configs)
  
  # handle other configs 
  case
  when block_given?
    yield(other_configs) 
  when !other_configs.empty?
    log(:warn, "ignoring non-env configs: #{other_configs.keys.join(',')}", Logger::DEBUG)
  end
  
  self
end

#recursive_each(*args, &block) ⇒ Object

Visits each nested env in order, starting with self, and passing to the block the env and any arguments generated by the parent of the env. The initial arguments are set when recursive_each is first called; subsequent arguements are the return values of the block.

e0, e1, e2, e3, e4 = ('a'..'e').collect {|name| Tap::Env.new(:name => name) }

e0.push(e1).push(e2)
e1.push(e3).push(e4)

lines = []
e0.recursive_each(0) do |env, nesting_depth|
  lines << "\n#{'..' * nesting_depth}#{env.config[:name]} (#{nesting_depth})"
  nesting_depth + 1
end

lines.join
# => %Q{
# a (0)
# ..b (1)
# ....d (2)
# ....e (2)
# ..c (1)}


299
300
301
# File 'lib/tap/env.rb', line 299

def recursive_each(*args, &block) # :yields: env, *parent_args
  each_nested_env(self, [], args, &block)
end

#recursive_inspect(template = nil, *args) ⇒ Object

:yields: templater, attrs



544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
# File 'lib/tap/env.rb', line 544

def recursive_inspect(template=nil, *args) # :yields: templater, attrs
  return "#<#{self.class}:#{object_id} root='#{root.root}'>" if template == nil
  
  attrs = {}
  templaters = []
  recursive_each(*args) do |env, *argv|
    templater = Support::Templater.new(template, :env => env)
    next_args = block_given? ? yield(templater, attrs, *argv) : argv
    templaters << templater if next_args
    
    next_args
  end
  
  templaters.collect do |templater|
    templater.build(attrs)
  end.join
end

#reverse_eachObject

Passes each nested env to the block in reverse order, ending with self.



270
271
272
# File 'lib/tap/env.rb', line 270

def reverse_each
  envs(true).reverse_each {|e| yield(e) }
end

#search_path(dir, path) ⇒ Object

Searches each env for the first existing file or directory at env.root.filepath(dir, path). Paths are expanded, and search_path checks to make sure the file is, in fact, relative to env.root. An optional block may be used to check the file; the file will only be returned if the block returns true.

Returns nil if no file can be found.



457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
# File 'lib/tap/env.rb', line 457

def search_path(dir, path)
  each do |env|
    directory = env.root.filepath(dir)
    file = env.root.filepath(dir, path)
    
    # check the file is relative to the
    # directory, and that the file exists.
    if file.rindex(directory, 0) == 0 && 
      File.exists?(file) && 
      (!block_given? || yield(file))
      return file
    end
  end
  
  nil
end

#summarize(name, template = ) ⇒ Object



504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
# File 'lib/tap/env.rb', line 504

def summarize(name, template=TEMPLATES[name])
  count = 0
  width = 10
  
  env_names = {}
  minimap.each do |env_name, env|
    env_names[env] = env_name
  end
  
  inspect(template) do |templater, share|
    env = templater.env
    entries = env.send(name).minimap
    next(false) if entries.empty?
    
    templater.env_name = env_names[env]
    templater.entries = entries
    
    count += 1
    entries.each do |entry_name, entry|
      width = entry_name.length if width < entry_name.length
    end
    
    share[:count] = count
    share[:width] = width
    true
  end
end

#unshift(env) ⇒ Object

Unshifts env onto envs, removing duplicates.

Self cannot be unshifted onto self.



247
248
249
250
251
252
# File 'lib/tap/env.rb', line 247

def unshift(env)
  unless env == self || envs[0] == env
    self.envs = envs.dup.unshift(env)
  end
  self
end