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



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

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

#changed_librariesObject



121
122
123
124
# File 'lib/boson/repo_index.rb', line 121

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

#delete_stale_libraries_and_commandsObject



80
81
82
83
84
85
86
# File 'lib/boson/repo_index.rb', line 80

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)


64
65
66
# File 'lib/boson/repo_index.rb', line 64

def exists?
  File.exists? marshal_file
end

#find_library(command, object = false) ⇒ Object



111
112
113
114
115
116
117
118
119
# File 'lib/boson/repo_index.rb', line 111

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



126
127
128
129
130
131
132
# File 'lib/boson/repo_index.rb', line 126

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

#marshal_fileObject



107
108
109
# File 'lib/boson/repo_index.rb', line 107

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

#namespacesObject



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

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
42
43
44
45
# File 'lib/boson/repo_index.rb', line 35

def read
  return if @read
  @libraries, @commands, @lib_hashes = exists? ?
    File.open(marshal_file, 'rb') do |f|
      f.flock(File::LOCK_EX)
      Marshal.load(f)
    end : [[], [], {}]
  delete_stale_libraries_and_commands
  set_command_namespaces
  @read = true
end

#read_and_transfer(ignored_libraries = []) ⇒ Object

:stopdoc:



55
56
57
58
59
60
61
62
# File 'lib/boson/repo_index.rb', line 55

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



68
69
70
71
72
73
74
75
76
77
78
# File 'lib/boson/repo_index.rb', line 68

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



89
90
91
92
93
94
95
# File 'lib/boson/repo_index.rb', line 89

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.



48
49
50
51
52
# File 'lib/boson/repo_index.rb', line 48

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