Class: Boson::RepoIndex

Inherits:
Object
  • Object
show all
Defined in:
lib/boson/repo_index.rb

Overview

This class provides an index for commands and libraries of a given a Repo. When this index updates, it detects library files whose md5 hash have changed and reindexes them. The index is stored with Marshal at config/index.marshal (relative to a Repo’s root directory). Since the index is marshaled, putting lambdas/procs in it will break it.If an index gets corrupted, simply delete it and next time Boson needs it, the index will be recreated.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(repo) ⇒ RepoIndex

Returns a new instance of RepoIndex.



11
12
13
# File 'lib/boson/repo_index.rb', line 11

def initialize(repo)
  @repo = repo
end

Instance Attribute Details

#commandsObject (readonly)

Returns the value of attribute commands.



10
11
12
# File 'lib/boson/repo_index.rb', line 10

def commands
  @commands
end

#librariesObject (readonly)

Returns the value of attribute libraries.



10
11
12
# File 'lib/boson/repo_index.rb', line 10

def libraries
  @libraries
end

#repoObject (readonly)

Returns the value of attribute repo.



10
11
12
# File 'lib/boson/repo_index.rb', line 10

def repo
  @repo
end

Instance Method Details

#all_main_methodsObject



99
100
101
# File 'lib/boson/repo_index.rb', line 99

def all_main_methods
  @commands.reject {|e| e.namespace }.map {|e| [e.name, e.alias]}.flatten.compact + namespaces
end

#changed_librariesObject



117
118
119
120
# File 'lib/boson/repo_index.rb', line 117

def changed_libraries
  read
  latest_hashes.select {|lib, hash| @lib_hashes[lib] != hash}.map {|e| e[0]}
end

#delete_stale_libraries_and_commandsObject



76
77
78
79
80
81
82
# File 'lib/boson/repo_index.rb', line 76

def delete_stale_libraries_and_commands
  cached_libraries = @lib_hashes.keys
  libs_to_delete = @libraries.select {|e| !cached_libraries.include?(e.name) && e.is_a?(FileLibrary) }
  names_to_delete = libs_to_delete.map {|e| e.name }
  libs_to_delete.each {|e| @libraries.delete(e) }
  @commands.delete_if {|e| names_to_delete.include? e.lib }
end

#exists?Boolean

Returns:

  • (Boolean)


60
61
62
# File 'lib/boson/repo_index.rb', line 60

def exists?
  File.exists? marshal_file
end

#find_library(command, object = false) ⇒ Object



107
108
109
110
111
112
113
114
115
# File 'lib/boson/repo_index.rb', line 107

def find_library(command, object=false)
  read
  namespace_command = command.split(NAMESPACE)[0]
  if (lib = @libraries.find {|e| e.namespace == namespace_command })
    object ? lib : lib.name
  elsif (cmd = Command.find(command, @commands))
    object ? @libraries.find {|e| e.name == cmd.lib} : cmd.lib
  end
end

#latest_hashesObject



122
123
124
125
126
127
128
# File 'lib/boson/repo_index.rb', line 122

def latest_hashes
  repo.all_libraries.inject({}) {|h, e|
    lib_file = FileLibrary.library_file(e, repo.dir)
    h[e] = Digest::MD5.hexdigest(File.read(lib_file)) if File.exists?(lib_file)
    h
  }
end

#load_index_from_fileObject



130
131
132
133
134
135
# File 'lib/boson/repo_index.rb', line 130

def load_index_from_file
  File.open(marshal_file, 'rb') do |f|
    f.flock(File::LOCK_EX)
    Marshal.load(f)
  end
end

#marshal_fileObject



103
104
105
# File 'lib/boson/repo_index.rb', line 103

def marshal_file
  File.join(repo.config_dir, 'index.marshal')
end

#namespacesObject



93
94
95
96
97
# File 'lib/boson/repo_index.rb', line 93

def namespaces
  nsps = @libraries.map {|e| e.namespace }.compact
  nsps.delete(false)
  nsps
end

#readObject

Reads and initializes index.



35
36
37
38
39
40
41
# File 'lib/boson/repo_index.rb', line 35

def read
  return if @read
  @libraries, @commands, @lib_hashes = exists? ? load_index_from_file : [[], [], {}]
  delete_stale_libraries_and_commands
  set_command_namespaces
  @read = true
end

#read_and_transfer(ignored_libraries = []) ⇒ Object

:stopdoc:



51
52
53
54
55
56
57
58
# File 'lib/boson/repo_index.rb', line 51

def read_and_transfer(ignored_libraries=[])
  read
  existing_libraries = (Boson.libraries.map {|e| e.name} + ignored_libraries).uniq
  libraries_to_add = @libraries.select {|e| !existing_libraries.include?(e.name)}
  Boson.libraries += libraries_to_add
  # depends on saved commands being correctly associated with saved libraries
  Boson.commands += libraries_to_add.map {|e| e.command_objects(e.commands, @commands) }.flatten
end

#save_marshal_index(marshal_string) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
# File 'lib/boson/repo_index.rb', line 64

def save_marshal_index(marshal_string)
  binmode = defined?(File::BINARY) ? File::BINARY : 0
  rdwr_access = File::RDWR | File::CREAT | binmode
  # To protect the file truncing with a lock we cannot use the 'wb' options.
  # The w option truncates the file before calling the File.open block
  File.open(marshal_file, rdwr_access) do |f|
    f.flock(File::LOCK_EX)
    f.truncate 0
    f.write(marshal_string)
  end
end

#set_command_namespacesObject

set namespaces for commands



85
86
87
88
89
90
91
# File 'lib/boson/repo_index.rb', line 85

def set_command_namespaces
  lib_commands = @commands.inject({}) {|t,e| (t[e.lib] ||= []) << e; t }
  namespace_libs = @libraries.select {|e| e.namespace(e.indexed_namespace) }
  namespace_libs.each {|lib|
    (lib_commands[lib.name] || []).each {|e| e.namespace = lib.namespace }
  }
end

#update(options = {}) ⇒ Object

Updates the index.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/boson/repo_index.rb', line 16

def update(options={})
  libraries_to_update = !exists? ? repo.all_libraries : options[:libraries] || changed_libraries
  read_and_transfer(libraries_to_update)
  if options[:verbose]
    puts !exists? ? "Generating index for all #{libraries_to_update.size} libraries. Patience ... is a bitch" :
      (libraries_to_update.empty? ? "No libraries indexed" :
      "Indexing the following libraries: #{libraries_to_update.join(', ')}")
  end
  Manager.instance.failed_libraries = []
  unless libraries_to_update.empty?
    Manager.load(libraries_to_update, options.merge(:index=>true))
    unless Manager.instance.failed_libraries.empty?
      $stderr.puts("Error: These libraries failed to load while indexing: #{Manager.instance.failed_libraries.join(', ')}")
    end
  end
  write(Manager.instance.failed_libraries)
end

#write(failed_libraries = []) ⇒ Object

Writes/saves current index to config/index.marshal.



44
45
46
47
48
# File 'lib/boson/repo_index.rb', line 44

def write(failed_libraries=[])
  latest = latest_hashes
  failed_libraries.each {|e| latest.delete(e) }
  save_marshal_index Marshal.dump([Boson.libraries, Boson.commands, latest])
end