Class: BitClust::MethodDatabase

Inherits:
Database show all
Includes:
Completion
Defined in:
lib/bitclust/methoddatabase.rb

Overview

Database for Ruby entries (library, class, method).

Constant Summary

Constants included from NameUtils

NameUtils::CHAR_TO_MARK, NameUtils::CHAR_TO_NAME, NameUtils::CLASS_NAME_RE, NameUtils::CLASS_PATH_RE, NameUtils::CONST_PATH_RE, NameUtils::CONST_RE, NameUtils::GVAR_RE, NameUtils::LIBNAME_RE, NameUtils::MARK_TO_CHAR, NameUtils::MARK_TO_NAME, NameUtils::METHOD_NAME_RE, NameUtils::METHOD_SPEC_RE, NameUtils::MID, NameUtils::NAME_TO_CHAR, NameUtils::NAME_TO_MARK, NameUtils::TYPEMARK_RE

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Database

#atomic_write_open, #dummy?, #encoding, #entries, #exist?, #foreach_line, #load_properties, #makepath, #properties, #propget, #propkeys, #propset, #read, #save_properties, #transaction

Methods included from NameUtils

build_method_id, classid2name, classname2id, classname?, decodeid, decodename_fs, decodename_url, display_typemark, encodeid, encodename_fs, encodename_rdocurl, encodename_url, functionname?, gvarname?, html_filename, libid2name, libname2id, libname?, method_spec?, methodid2classid, methodid2libid, methodid2mname, methodid2specparts, methodid2specstring, methodid2typechar, methodid2typemark, methodid2typename, methodname?, split_method_id, split_method_spec, typechar2mark, typechar2name, typechar?, typemark2char, typemark2name, typemark?, typename2char, typename2mark, typename?

Constructor Details

#initialize(prefix) ⇒ MethodDatabase

Returns a new instance of MethodDatabase.



36
37
38
39
40
41
42
43
44
45
46
# File 'lib/bitclust/methoddatabase.rb', line 36

def initialize(prefix)
  super prefix
  @librarymap = nil
  @classmap = {}
  @class_extent_loaded = false
  @in_transaction = false
  @dirty_libraries = {}
  @dirty_classes = {}
  @dirty_methods = {}
  @refs = nil
end

Instance Attribute Details

#refsObject



217
218
219
# File 'lib/bitclust/methoddatabase.rb', line 217

def refs
  @refs ||= RefsDatabase.load(realpath('refs'))
end

Instance Method Details

#classesObject

Classe Entry



376
377
378
# File 'lib/bitclust/methoddatabase.rb', line 376

def classes
  classmap().values
end

#clear_dirtyObject



127
128
129
130
131
# File 'lib/bitclust/methoddatabase.rb', line 127

def clear_dirty
  @dirty_libraries.clear
  @dirty_classes.clear
  @dirty_methods.clear
end

#copy_docObject



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/bitclust/methoddatabase.rb', line 230

def copy_doc
  return copy_doc_md if @md_root
  return unless @root
  root_path = Pathname.new(@root).expand_path
  Dir.glob("#{@root}/../../doc/**/*.rd").each do |f|
    if %r!\A#{Regexp.escape(@root)}/\.\./\.\./doc/([-\./\w]+)\.rd\z! =~ f
      id = libname2id($1 || raise)
      se = DocEntry.new(self, id)
      s = Preprocessor.read(f, properties)
      title, source = RRDParser.split_doc(s)
      relative_path = Pathname.new(f).expand_path(@root).relative_path_from(root_path)
      se.title = title
      se.source = source
      se.source_location = Location.new(relative_path, 1)
      se.save
    end
  end
end

#dirty?Boolean

Returns:

  • (Boolean)


91
92
93
94
95
# File 'lib/bitclust/methoddatabase.rb', line 91

def dirty?
  not @dirty_libraries.empty? or
  not @dirty_classes.empty? or
  not @dirty_methods.empty?
end

#dirty_class(c) ⇒ Object



111
112
113
# File 'lib/bitclust/methoddatabase.rb', line 111

def dirty_class(c)
  @dirty_classes[c] = true
end

#dirty_library(lib) ⇒ Object



103
104
105
# File 'lib/bitclust/methoddatabase.rb', line 103

def dirty_library(lib)
  @dirty_libraries[lib] = true
end

#dirty_method(m) ⇒ Object



119
120
121
# File 'lib/bitclust/methoddatabase.rb', line 119

def dirty_method(m)
  @dirty_methods[m] = true
end

#docsObject

Doc Entry



312
313
314
# File 'lib/bitclust/methoddatabase.rb', line 312

def docs
  docmap().values
end

#each_dirty_class(&block) ⇒ Object



115
116
117
# File 'lib/bitclust/methoddatabase.rb', line 115

def each_dirty_class(&block)
  @dirty_classes.each_key(&block)
end

#each_dirty_entry(&block) ⇒ Object



97
98
99
100
101
# File 'lib/bitclust/methoddatabase.rb', line 97

def each_dirty_entry(&block)
  (@dirty_libraries.keys +
   @dirty_classes.keys +
   @dirty_methods.keys).each(&block)
end

#each_dirty_library(&block) ⇒ Object



107
108
109
# File 'lib/bitclust/methoddatabase.rb', line 107

def each_dirty_library(&block)
  @dirty_libraries.each_key(&block)
end

#each_dirty_method(&block) ⇒ Object



123
124
125
# File 'lib/bitclust/methoddatabase.rb', line 123

def each_dirty_method(&block)
  @dirty_methods.each_key(&block)
end

#fetch_class(name) ⇒ Object

This method does not work in transaction.



403
404
405
406
407
408
# File 'lib/bitclust/methoddatabase.rb', line 403

def fetch_class(name)
  id = intern_classname(name) or
      raise ClassNotFound, "class not found: #{name.inspect}"
  load_class(id) or
      raise "must not happen: #{name.inspect}, #{id.inspect}"
end

#fetch_class_id(id) ⇒ Object



410
411
412
413
# File 'lib/bitclust/methoddatabase.rb', line 410

def fetch_class_id(id)
  load_class(id) or
      raise ClassNotFound, "class not found: #{id.inspect}"
end

#fetch_doc(name) ⇒ Object



326
327
328
329
# File 'lib/bitclust/methoddatabase.rb', line 326

def fetch_doc(name)
  docmap()[libname2id(name)] or
      raise DocNotFound, "doc not found: #{name.inspect}"
end

#fetch_library(name) ⇒ Object



349
350
351
352
# File 'lib/bitclust/methoddatabase.rb', line 349

def fetch_library(name)
  librarymap()[libname2id(name)] or
      raise LibraryNotFound, "library not found: #{name.inspect}"
end

#fetch_library_id(id) ⇒ Object



354
355
356
357
# File 'lib/bitclust/methoddatabase.rb', line 354

def fetch_library_id(id)
  librarymap()[id] or
      raise LibraryNotFound, "library not found: #{id.inspect}"
end

#fetch_method(spec) ⇒ Object



495
496
497
# File 'lib/bitclust/methoddatabase.rb', line 495

def fetch_method(spec)
  fetch_class(spec.klass).fetch_method(spec)
end

#fetch_methods(spec) ⇒ Object



491
492
493
# File 'lib/bitclust/methoddatabase.rb', line 491

def fetch_methods(spec)
  fetch_class(spec.klass).fetch_methods(spec)
end

#get_class(name) ⇒ Object

get a class entry if it exists, or create a new class entry object if it doesn't exist.



392
393
394
395
396
397
398
399
400
# File 'lib/bitclust/methoddatabase.rb', line 392

def get_class(name)
  if id = intern_classname(name)
    load_class(id) or
        raise "must not happen: #{name.inspect}, #{id.inspect}"
  else
    id = classname2id(name)
    @classmap[id] ||= ClassEntry.new(self, id)
  end
end

#get_doc(name) ⇒ Object



321
322
323
324
# File 'lib/bitclust/methoddatabase.rb', line 321

def get_doc(name)
  id = libname2id(name)
  docmap()[id] ||= DocEntry.new(self, id)
end

#get_library(name) ⇒ Object



344
345
346
347
# File 'lib/bitclust/methoddatabase.rb', line 344

def get_library(name)
  id = libname2id(name)
  librarymap()[id] ||= LibraryEntry.new(self, id)
end

#get_method(spec) ⇒ Object



487
488
489
# File 'lib/bitclust/methoddatabase.rb', line 487

def get_method(spec)
  get_class(spec.klass).get_method(spec)
end

#initObject



50
51
52
53
54
55
56
57
58
59
60
# File 'lib/bitclust/methoddatabase.rb', line 50

def init
  prefix = @prefix || raise
  FileUtils.rm_rf prefix
  FileUtils.mkdir_p prefix
  Dir.mkdir "#{prefix}/library"
  Dir.mkdir "#{prefix}/class"
  Dir.mkdir "#{prefix}/method"
  Dir.mkdir "#{prefix}/doc"
  FileUtils.touch "#{prefix}/properties"
  FileUtils.touch "#{prefix}/refs"
end

#librariesObject

Library Entry



335
336
337
# File 'lib/bitclust/methoddatabase.rb', line 335

def libraries
  librarymap().values
end

#make_refsObject



221
222
223
224
225
226
227
228
# File 'lib/bitclust/methoddatabase.rb', line 221

def make_refs
  [classes, libraries, methods, docs].each do |es|
    es.each do |e|
      refs().extract(e)
    end
  end
  refs
end

#methodsObject



483
484
485
# File 'lib/bitclust/methoddatabase.rb', line 483

def methods
  classes().map {|c| c.entries }.flatten
end

#open_class(name) {|c| ... } ⇒ Object

Yields:

  • (c)


423
424
425
426
427
428
429
430
431
432
433
434
435
# File 'lib/bitclust/methoddatabase.rb', line 423

def open_class(name)
  check_transaction
  id = classname2id(name)
  if exist?("class/#{id}")
    c = load_class(id) || raise
    c.clear
  else
    c = (@classmap[id] ||= ClassEntry.new(self, id))
  end
  yield c
  dirty_class c
  c
end

#open_library(name, reopen = false) ⇒ Object



359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/bitclust/methoddatabase.rb', line 359

def open_library(name, reopen = false)
  check_transaction
  map = librarymap()
  id = libname2id(name)
  if lib = map[id]
    lib.clear unless reopen
  else
    lib = (map[id] ||= LibraryEntry.new(self, id))
  end
  dirty_library lib
  lib
end

#open_method(id) {|m| ... } ⇒ Object

Return existing/newly created MethodEntry from the given MethodID

FIXME: see kind

Yields:

  • (m)


468
469
470
471
472
473
474
475
476
477
478
479
480
481
# File 'lib/bitclust/methoddatabase.rb', line 468

def open_method(id)
  check_transaction
  if m = id.klass.get_method(id)
    m.clear
  else
    m = MethodEntry.new(self, id.idstring)
    id.klass.add_method m
  end
  m.library = id.library
  m.klass   = id.klass
  yield m
  dirty_method m
  m
end

#search_classes(pattern) ⇒ Object



415
416
417
418
419
420
421
# File 'lib/bitclust/methoddatabase.rb', line 415

def search_classes(pattern)
  cs = _search_classes(pattern)
  if cs.empty?
    raise ClassNotFound, "no such class: #{pattern}"
  end
  cs
end

#search_methods(pattern) ⇒ Object



499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
# File 'lib/bitclust/methoddatabase.rb', line 499

def search_methods(pattern)
  result = _search_methods(pattern)
  if result.fail?
    if result.classes.empty?
      loc = pattern.klass ? pattern.klass + '.' : ''
      raise MethodNotFound, "no such method: #{loc}#{pattern.method}"
    end
    if result.classes.size <= 5
      loc = result.classes.map {|c| c.label }.join(', ')
    else
      loc = "#{result.classes.size} classes"
    end
    raise MethodNotFound, "no such method in #{loc}: #{pattern.method}"
  end
  result
end

#update_by_file(path, libname) ⇒ Object



162
163
164
165
# File 'lib/bitclust/methoddatabase.rb', line 162

def update_by_file(path, libname)
  check_transaction
  RRDParser.new(self).parse_file(path, libname, properties())
end

#update_by_markdowntree(md_root) ⇒ Object

Markdown ツリー(manual/api)を直接パースして DB を更新する(M3)。 front matter の library: が所属を表すため、ライブラリごとに lib ファイル → メンバーファイル(reopen/redefine のみのファイルは後置。 dynamic include の対象 module が先に定義されている必要があるため)の 順でパースする。版ゲート(since/until)外のライブラリ/メンバーはスキップ。 エントリの source には md 断片が入り、source_location は md の実パスを指す



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/bitclust/methoddatabase.rb', line 173

def update_by_markdowntree(md_root)
  require 'bitclust/markdown_tree'
  require 'bitclust/mdparser'
  @md_root = md_root
  # 描画層(screen.rb)が MDCompiler を選択するためのマーカー
  propset 'source_format', 'markdown'
  tree = MarkdownTree.scan(md_root)
  version = properties()["version"]
  tree.libraries.sort.each do |libname, lib|
    next unless md_version_covers?(version, lib[:since], lib[:until])
    lib_entry = MDParser.new(self).parse_file(File.join(md_root, lib[:path]), libname, properties())
    lib_location = lib_entry.source_location
    # 多重所属(ゲート付き library リスト)は、この版でゲートが生きている
    # membership を持つライブラリだけがメンバーとして取り込む
    members = tree.entities.select { |_, e|
      e[:memberships].any? { |m|
        m[:library] == libname && md_version_covers?(version, m[:since], m[:until])
      }
    }
    sorted = members.keys.sort_by { |path|
      reopen_only = members[path][:kinds].all? { |kind, _| %w[reopen redefine].include?(kind) }
      [reopen_only ? 1 : 0, path]
    }
    sorted.each do |path|
      entity = members[path]
      next unless md_version_covers?(version, entity[:since], entity[:until])
      MDParser.new(self).parse_file(File.join(md_root, path), libname, properties())
    end
    # parse_file は毎回 library の source_location を上書きするため
    # lib ファイルの値へ戻す
    lib_entry.source_location = lib_location
  end
end

#update_by_stdlibtree(root) ⇒ Object



148
149
150
151
152
153
# File 'lib/bitclust/methoddatabase.rb', line 148

def update_by_stdlibtree(root)
  @root = root
  parse_LIBRARIES("#{root}/LIBRARIES", properties()).each do |libname|
    update_by_file "#{root}/#{libname}.rd", libname
  end
end

#update_requiresObject



133
134
135
136
137
# File 'lib/bitclust/methoddatabase.rb', line 133

def update_requires
  libraries.each{|lib|
    lib.requires = lib.all_requires
  }
end

#update_sublibrariesObject



139
140
141
142
143
144
145
146
# File 'lib/bitclust/methoddatabase.rb', line 139

def update_sublibraries
  libraries.each{|lib|
    re = /\A#{lib.name}\// 
    libraries.each{|l|
      lib.sublibrary(l) if re =~ l.name
    }
  }
end