yoda-language-server 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +8 -0
  5. data/Gemfile +6 -0
  6. data/Gemfile.lock +78 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +85 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/client/atom/main.js +27 -0
  13. data/client/vscode/.gitignore +4 -0
  14. data/client/vscode/.vscode/launch.json +28 -0
  15. data/client/vscode/.vscode/settings.json +9 -0
  16. data/client/vscode/.vscode/tasks.json +20 -0
  17. data/client/vscode/.vscodeignore +8 -0
  18. data/client/vscode/CHANGELOG.md +7 -0
  19. data/client/vscode/README.md +65 -0
  20. data/client/vscode/package-lock.json +2688 -0
  21. data/client/vscode/package.json +39 -0
  22. data/client/vscode/src/extension.ts +42 -0
  23. data/client/vscode/src/test/extension.test.ts +22 -0
  24. data/client/vscode/src/test/index.ts +22 -0
  25. data/client/vscode/tsconfig.json +16 -0
  26. data/client/vscode/vsc-extension-quickstart.md +33 -0
  27. data/exe/yoda +27 -0
  28. data/lib/yoda.rb +11 -0
  29. data/lib/yoda/evaluation.rb +9 -0
  30. data/lib/yoda/evaluation/code_completion.rb +65 -0
  31. data/lib/yoda/evaluation/code_completion/base_provider.rb +57 -0
  32. data/lib/yoda/evaluation/code_completion/const_provider.rb +90 -0
  33. data/lib/yoda/evaluation/code_completion/method_provider.rb +82 -0
  34. data/lib/yoda/evaluation/code_completion/variable_provider.rb +18 -0
  35. data/lib/yoda/evaluation/comment_completion.rb +70 -0
  36. data/lib/yoda/evaluation/comment_completion/base_provider.rb +64 -0
  37. data/lib/yoda/evaluation/comment_completion/param_provider.rb +18 -0
  38. data/lib/yoda/evaluation/comment_completion/tag_provider.rb +41 -0
  39. data/lib/yoda/evaluation/comment_completion/type_provider.rb +58 -0
  40. data/lib/yoda/evaluation/current_node_explain.rb +70 -0
  41. data/lib/yoda/evaluation/evaluator.rb +103 -0
  42. data/lib/yoda/evaluation/signature_discovery.rb +83 -0
  43. data/lib/yoda/model.rb +12 -0
  44. data/lib/yoda/model/completion_item.rb +56 -0
  45. data/lib/yoda/model/descriptions.rb +10 -0
  46. data/lib/yoda/model/descriptions/base.rb +26 -0
  47. data/lib/yoda/model/descriptions/function_description.rb +40 -0
  48. data/lib/yoda/model/descriptions/value_description.rb +33 -0
  49. data/lib/yoda/model/descriptions/word_description.rb +32 -0
  50. data/lib/yoda/model/function_signatures.rb +13 -0
  51. data/lib/yoda/model/function_signatures/base.rb +68 -0
  52. data/lib/yoda/model/function_signatures/constructor.rb +70 -0
  53. data/lib/yoda/model/function_signatures/formatter.rb +82 -0
  54. data/lib/yoda/model/function_signatures/method.rb +67 -0
  55. data/lib/yoda/model/function_signatures/overload.rb +79 -0
  56. data/lib/yoda/model/function_signatures/parameter_list.rb +108 -0
  57. data/lib/yoda/model/function_signatures/type_builder.rb +101 -0
  58. data/lib/yoda/model/node_signature.rb +28 -0
  59. data/lib/yoda/model/path.rb +96 -0
  60. data/lib/yoda/model/scoped_path.rb +44 -0
  61. data/lib/yoda/model/types.rb +84 -0
  62. data/lib/yoda/model/types/any_type.rb +32 -0
  63. data/lib/yoda/model/types/base.rb +37 -0
  64. data/lib/yoda/model/types/duck_type.rb +41 -0
  65. data/lib/yoda/model/types/function_type.rb +174 -0
  66. data/lib/yoda/model/types/generic_type.rb +66 -0
  67. data/lib/yoda/model/types/instance_type.rb +42 -0
  68. data/lib/yoda/model/types/module_type.rb +42 -0
  69. data/lib/yoda/model/types/sequence_type.rb +53 -0
  70. data/lib/yoda/model/types/union_type.rb +56 -0
  71. data/lib/yoda/model/types/unknown_type.rb +40 -0
  72. data/lib/yoda/model/types/value_type.rb +58 -0
  73. data/lib/yoda/model/values.rb +9 -0
  74. data/lib/yoda/model/values/base.rb +32 -0
  75. data/lib/yoda/model/values/instance_value.rb +65 -0
  76. data/lib/yoda/model/values/module_value.rb +72 -0
  77. data/lib/yoda/parsing.rb +15 -0
  78. data/lib/yoda/parsing/ast_traversable.rb +18 -0
  79. data/lib/yoda/parsing/comment_tokenizer.rb +59 -0
  80. data/lib/yoda/parsing/location.rb +101 -0
  81. data/lib/yoda/parsing/node_objects.rb +10 -0
  82. data/lib/yoda/parsing/node_objects/const_node.rb +52 -0
  83. data/lib/yoda/parsing/node_objects/method_definition.rb +46 -0
  84. data/lib/yoda/parsing/node_objects/namespace.rb +104 -0
  85. data/lib/yoda/parsing/node_objects/send_node.rb +72 -0
  86. data/lib/yoda/parsing/parser.rb +27 -0
  87. data/lib/yoda/parsing/query.rb +11 -0
  88. data/lib/yoda/parsing/query/current_comment_query.rb +80 -0
  89. data/lib/yoda/parsing/query/current_comment_token_query.rb +153 -0
  90. data/lib/yoda/parsing/query/current_commenting_node_query.rb +68 -0
  91. data/lib/yoda/parsing/query/current_location_node_query.rb +51 -0
  92. data/lib/yoda/parsing/query/current_node_comment_query.rb +40 -0
  93. data/lib/yoda/parsing/range.rb +41 -0
  94. data/lib/yoda/parsing/scopes.rb +15 -0
  95. data/lib/yoda/parsing/scopes/base.rb +78 -0
  96. data/lib/yoda/parsing/scopes/builder.rb +60 -0
  97. data/lib/yoda/parsing/scopes/class_definition.rb +47 -0
  98. data/lib/yoda/parsing/scopes/meta_class_definition.rb +44 -0
  99. data/lib/yoda/parsing/scopes/meta_method_definition.rb +70 -0
  100. data/lib/yoda/parsing/scopes/method_definition.rb +69 -0
  101. data/lib/yoda/parsing/scopes/module_definition.rb +36 -0
  102. data/lib/yoda/parsing/scopes/root.rb +25 -0
  103. data/lib/yoda/parsing/source_analyzer.rb +59 -0
  104. data/lib/yoda/parsing/source_cutter.rb +231 -0
  105. data/lib/yoda/parsing/type_parser.rb +141 -0
  106. data/lib/yoda/runner.rb +6 -0
  107. data/lib/yoda/runner/infer.rb +50 -0
  108. data/lib/yoda/runner/setup.rb +26 -0
  109. data/lib/yoda/server.rb +191 -0
  110. data/lib/yoda/server/client_info.rb +98 -0
  111. data/lib/yoda/server/completion_provider.rb +78 -0
  112. data/lib/yoda/server/definition_provider.rb +36 -0
  113. data/lib/yoda/server/deserializer.rb +27 -0
  114. data/lib/yoda/server/hover_provider.rb +38 -0
  115. data/lib/yoda/server/signature_provider.rb +46 -0
  116. data/lib/yoda/store.rb +13 -0
  117. data/lib/yoda/store/actions.rb +10 -0
  118. data/lib/yoda/store/actions/import_core_library.rb +30 -0
  119. data/lib/yoda/store/actions/import_gems.rb +91 -0
  120. data/lib/yoda/store/actions/read_file.rb +36 -0
  121. data/lib/yoda/store/actions/read_project_files.rb +29 -0
  122. data/lib/yoda/store/adapters.rb +14 -0
  123. data/lib/yoda/store/adapters/base.rb +58 -0
  124. data/lib/yoda/store/adapters/leveldb_adapter.rb +80 -0
  125. data/lib/yoda/store/adapters/lmdb_adapter.rb +113 -0
  126. data/lib/yoda/store/objects.rb +46 -0
  127. data/lib/yoda/store/objects/addressable.rb +25 -0
  128. data/lib/yoda/store/objects/base.rb +116 -0
  129. data/lib/yoda/store/objects/class_object.rb +51 -0
  130. data/lib/yoda/store/objects/merger.rb +94 -0
  131. data/lib/yoda/store/objects/meta_class_object.rb +41 -0
  132. data/lib/yoda/store/objects/method_object.rb +94 -0
  133. data/lib/yoda/store/objects/module_object.rb +11 -0
  134. data/lib/yoda/store/objects/namespace_object.rb +67 -0
  135. data/lib/yoda/store/objects/overload.rb +51 -0
  136. data/lib/yoda/store/objects/patch.rb +46 -0
  137. data/lib/yoda/store/objects/patch_set.rb +80 -0
  138. data/lib/yoda/store/objects/tag.rb +62 -0
  139. data/lib/yoda/store/objects/value_object.rb +45 -0
  140. data/lib/yoda/store/project.rb +159 -0
  141. data/lib/yoda/store/query.rb +12 -0
  142. data/lib/yoda/store/query/associators.rb +10 -0
  143. data/lib/yoda/store/query/associators/associate_ancestors.rb +103 -0
  144. data/lib/yoda/store/query/associators/associate_methods.rb +38 -0
  145. data/lib/yoda/store/query/base.rb +16 -0
  146. data/lib/yoda/store/query/find_constant.rb +150 -0
  147. data/lib/yoda/store/query/find_meta_class.rb +18 -0
  148. data/lib/yoda/store/query/find_method.rb +74 -0
  149. data/lib/yoda/store/query/find_signature.rb +43 -0
  150. data/lib/yoda/store/registry.rb +67 -0
  151. data/lib/yoda/store/yard_importer.rb +260 -0
  152. data/lib/yoda/typing.rb +10 -0
  153. data/lib/yoda/typing/context.rb +96 -0
  154. data/lib/yoda/typing/environment.rb +35 -0
  155. data/lib/yoda/typing/evaluator.rb +256 -0
  156. data/lib/yoda/typing/lexical_scope.rb +26 -0
  157. data/lib/yoda/typing/relation.rb +15 -0
  158. data/lib/yoda/typing/traces.rb +9 -0
  159. data/lib/yoda/typing/traces/base.rb +26 -0
  160. data/lib/yoda/typing/traces/normal.rb +22 -0
  161. data/lib/yoda/typing/traces/send.rb +26 -0
  162. data/lib/yoda/version.rb +3 -0
  163. data/lib/yoda/yard_extensions.rb +11 -0
  164. data/lib/yoda/yard_extensions/sig_directive.rb +40 -0
  165. data/lib/yoda/yard_extensions/type_tag.rb +10 -0
  166. data/package.json +76 -0
  167. data/scripts/benchmark.rb +6 -0
  168. data/scripts/build_core_index.sh +16 -0
  169. data/yarn.lock +13 -0
  170. data/yoda-language-server.gemspec +40 -0
  171. metadata +424 -0
@@ -0,0 +1,36 @@
1
+ module Yoda
2
+ class Server
3
+ class DefinitionProvider
4
+ # @type ClientInfo
5
+ attr_reader :client_info
6
+
7
+ # @param client_info [ClientInfo]
8
+ def initialize(client_info)
9
+ @client_info = client_info
10
+ end
11
+
12
+ # @param uri [String]
13
+ # @param position [{Symbol => Integer}]
14
+ # @param include_declaration [Boolean]
15
+ def provide(uri, position, include_declaration = false)
16
+ source = client_info.file_store.get(uri)
17
+ location = Parsing::Location.of_language_server_protocol_position(line: position[:line], character: position[:character])
18
+
19
+ node_worker = Evaluation::CurrentNodeExplain.new(client_info.registry, source, location)
20
+ references = node_worker.defined_files
21
+ references.map { |(path, line, column)| create_location(path, line, column) }
22
+ end
23
+
24
+ # @param path [String]
25
+ # @param line [Integer]
26
+ # @param column [Integer]
27
+ def create_location(path, line, column)
28
+ location = Parsing::Location.new(row: line - 1, column: column)
29
+ LSP::Interface::Location.new(
30
+ uri: client_info.uri_of_path(path),
31
+ range: LSP::Interface::Range.new(Parsing::Range.new(location, location).to_language_server_protocol_range),
32
+ )
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,27 @@
1
+ require 'ostruct'
2
+
3
+ module Yoda
4
+ class Server
5
+ class Deserializer
6
+ def initialize
7
+ end
8
+
9
+ # @param params [Hash]
10
+ def deserialize(params)
11
+ Hash[params.map { |key, value| [snakenize(key), deserialize_value(value)] }]
12
+ end
13
+
14
+ # @param params [any]
15
+ def deserialize_value(value)
16
+ return deserialize(value) if value.is_a?(Hash)
17
+ return value.map { |el| deserialize_value(el) } if value.is_a?(Enumerable)
18
+ value
19
+ end
20
+
21
+ # @param str [Symbol]
22
+ def snakenize(str)
23
+ str.to_s.gsub(/([A-Z])/, '_\1').downcase.to_sym
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,38 @@
1
+ module Yoda
2
+ class Server
3
+ class HoverProvider
4
+ attr_reader :client_info
5
+
6
+ # @param client_info [ClientInfo]
7
+ def initialize(client_info)
8
+ @client_info = client_info
9
+ end
10
+
11
+ # @param uri [String]
12
+ # @param position [{Symbol => Integer}]
13
+ def request_hover(uri, position)
14
+ source = client_info.file_store.get(uri)
15
+ location = Parsing::Location.of_language_server_protocol_position(line: position[:line], character: position[:character])
16
+
17
+ node_worker = Evaluation::CurrentNodeExplain.new(client_info.registry, source, location)
18
+
19
+ current_node_signature = node_worker.current_node_signature
20
+ create_hover(current_node_signature) if current_node_signature
21
+ end
22
+
23
+ # @param signature [Evaluation::NodeSignature]
24
+ def create_hover(signature)
25
+ LSP::Interface::Hover.new(
26
+ contents: signature.descriptions.map { |value| create_hover_text(value) },
27
+ range: LSP::Interface::Range.new(signature.node_range.to_language_server_protocol_range),
28
+ )
29
+ end
30
+
31
+ # @param description [Evaluation::Descriptions::Base]
32
+ # @return [String]
33
+ def create_hover_text(description)
34
+ description.to_markdown
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,46 @@
1
+ module Yoda
2
+ class Server
3
+ class SignatureProvider
4
+ attr_reader :client_info
5
+
6
+ # @param client_info [ClientInfo]
7
+ def initialize(client_info)
8
+ @client_info = client_info
9
+ end
10
+
11
+ # @param uri [String]
12
+ # @param position [{Symbol => Integer}]
13
+ def provide(uri, position)
14
+ source = client_info.file_store.get(uri)
15
+ location = Parsing::Location.of_language_server_protocol_position(line: position[:line], character: position[:character])
16
+ cut_source = Parsing::SourceCutter.new(source, location).error_recovered_source
17
+
18
+ signature_worker = Evaluation::SignatureDiscovery.new(client_info.registry, cut_source, location)
19
+
20
+ functions = signature_worker.method_candidates
21
+ create_signature_help(functions)
22
+ end
23
+
24
+ # @param code_objects [Array<Model::FunctionSignatures::Base>]
25
+ def create_signature_help(functions)
26
+ signatures = functions.map { |func| Model::Descriptions::FunctionDescription.new(func) }
27
+ LSP::Interface::SignatureHelp.new(
28
+ signatures: signatures.map { |signature| create_signature_info(signature) },
29
+ )
30
+ end
31
+
32
+ # @param signature [Evaluation::Descriptions::FunctionDescription]
33
+ def create_signature_info(signature)
34
+ LSP::Interface::SignatureInformation.new(
35
+ label: signature.title.to_s,
36
+ documentation: signature.to_markdown,
37
+ parameters: signature.parameter_names.map do |parameter|
38
+ LSP::Interface::ParameterInformation.new(
39
+ label: parameter,
40
+ )
41
+ end,
42
+ )
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,13 @@
1
+ require 'yard'
2
+
3
+ module Yoda
4
+ module Store
5
+ require 'yoda/store/actions'
6
+ require 'yoda/store/adapters'
7
+ require 'yoda/store/project'
8
+ require 'yoda/store/registry'
9
+ require 'yoda/store/objects'
10
+ require 'yoda/store/query'
11
+ require 'yoda/store/yard_importer'
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ module Yoda
2
+ module Store
3
+ module Actions
4
+ require 'yoda/store/actions/import_core_library'
5
+ require 'yoda/store/actions/import_gems'
6
+ require 'yoda/store/actions/read_file'
7
+ require 'yoda/store/actions/read_project_files'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,30 @@
1
+ module Yoda
2
+ module Store
3
+ module Actions
4
+ class ImportCoreLibrary
5
+ # @return [Registry]
6
+ attr_reader :registry
7
+
8
+ def initialize(registry)
9
+ @registry = registry
10
+ end
11
+
12
+ def run
13
+ load_core_patches.each do |patch|
14
+ registry.add_patch(patch)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def load_core_patches
21
+ core_doc_files.map { |yardoc_file| YardImporter.import(yardoc_file) }
22
+ end
23
+
24
+ def core_doc_files
25
+ %W(.yoda/sources/ruby-#{RUBY_VERSION}/.yardoc .yoda/sources/ruby-#{RUBY_VERSION}/.yardoc-stdlib).map { |path| File.expand_path(path, '~') }.select { |path| File.exist?(path) }
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,91 @@
1
+ require 'open3'
2
+
3
+ module Yoda
4
+ module Store
5
+ module Actions
6
+ class ImportGems
7
+ # @return [Registry]
8
+ attr_reader :registry
9
+
10
+ # @return [Array<Bundler::LazySpecification>]
11
+ attr_reader :gem_specs
12
+
13
+ # @param registry [Registry]
14
+ # @param gem_specs [Array<Bundler::LazySpecification>]
15
+ def initialize(registry, gem_specs)
16
+ @registry = registry
17
+ @gem_specs = gem_specs
18
+ end
19
+
20
+ # @return [void]
21
+ def run
22
+ create_dependency_docs
23
+ gem_specs.map do |gem|
24
+ if yardoc_file = yardoc_file_for_gem(gem)
25
+ if patch = load_yardoc(yardoc_file, gem_path_for(gem))
26
+ registry.add_patch(patch)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def create_dependency_docs
35
+ gem_specs.each do |gem|
36
+ if yardoc_file_for_gem(gem)
37
+ STDERR.puts "Gem docs for #{gem.name} #{gem.version} already exist"
38
+ next
39
+ end
40
+ STDERR.puts "Building gem docs for #{gem.name} #{gem.version}"
41
+ begin
42
+ o, e = Open3.capture2e("yard gems #{gem.name} #{gem.version}")
43
+ STDERR.puts o unless o.empty?
44
+ if e.success?
45
+ STDERR.puts "Done building gem docs for #{gem.name} #{gem.version}"
46
+ else
47
+ STDERR.puts "Failed to build #{gem.name} #{gem.version}"
48
+ end
49
+ rescue => ex
50
+ STDERR.puts ex
51
+ STDERR.puts ex.backtrace
52
+ STDERR.puts "Failed to build #{gem.name} #{gem.version}"
53
+ end
54
+ end
55
+ end
56
+
57
+ # @param gem [Bundler::LazySpecification]
58
+ # @return [String, nil]
59
+ def yardoc_file_for_gem(gem)
60
+ YARD::Registry.yardoc_file_for_gem(gem.name, gem.version)
61
+ rescue Bundler::BundlerError => ex
62
+ STDERR.puts ex
63
+ STDERR.puts ex.backtrace
64
+ nil
65
+ end
66
+
67
+ # @param yardoc_file [String]
68
+ # @param gem_source_path [String]
69
+ # @return [Objects::Patch, nil]
70
+ def load_yardoc(yardoc_file, gem_source_path)
71
+ begin
72
+ YardImporter.import(yardoc_file, root_path: gem_source_path)
73
+ rescue => ex
74
+ STDERR.puts ex
75
+ STDERR.puts ex.backtrace
76
+ STDERR.puts "Failed to load #{yardoc_file}"
77
+ nil
78
+ end
79
+ end
80
+
81
+ # @param gem [Bundler::LazySpecification]
82
+ # @return [String, nil]
83
+ def gem_path_for(gem)
84
+ if spec = Gem.source_index.find_name(gem.name).first
85
+ spec.full_gem_path
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,36 @@
1
+ module Yoda
2
+ module Store
3
+ module Actions
4
+ class ReadFile
5
+ # @return [Registry]
6
+ attr_reader :registry
7
+
8
+ # @return [String]
9
+ attr_reader :file
10
+
11
+ # @param registry [Registry]
12
+ # @param file [String]
13
+ # @return [void]
14
+ def self.run(registry, file, root_path: nil)
15
+ self.new(registry, file, root_path: root_path).run
16
+ end
17
+
18
+ # @param registry [Registry]
19
+ # @param file [String]
20
+ def initialize(registry, file, root_path: nil)
21
+ @registry = registry
22
+ @file = file
23
+ @root_path = root_path
24
+ end
25
+
26
+ # @return [void]
27
+ def run
28
+ YARD::Registry.clear
29
+ YARD.parse([file])
30
+ patch = YardImporter.new(file).import(YARD::Registry.all + [YARD::Registry.root]).patch
31
+ registry.add_patch(patch)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,29 @@
1
+ module Yoda
2
+ module Store
3
+ module Actions
4
+ class ReadProjectFiles
5
+ # @return [Registry]
6
+ attr_reader :registry
7
+
8
+ # @return [String]
9
+ attr_reader :root_path
10
+
11
+ def initialize(registry, root_path)
12
+ @registry = registry
13
+ @root_path = root_path
14
+ end
15
+
16
+ def run
17
+ project_files.each { |file| ReadFile.run(registry, file) }
18
+ end
19
+
20
+ private
21
+
22
+ # @return [Array<String>]
23
+ def project_files
24
+ Dir.chdir(root_path) { Dir.glob("{lib,app}/**/*.rb\0ext/**/*.c\0.yoda/*.rb").map { |name| File.expand_path(name, root_path) } }
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,14 @@
1
+ module Yoda
2
+ module Store
3
+ module Adapters
4
+ require 'yoda/store/adapters/base'
5
+ require 'yoda/store/adapters/leveldb_adapter'
6
+ require 'yoda/store/adapters/lmdb_adapter'
7
+
8
+ # @return [Base.class]
9
+ def self.default_adapter_class
10
+ LmdbAdapter
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,58 @@
1
+ module Yoda
2
+ module Store
3
+ module Adapters
4
+ # @abstract
5
+ class Base
6
+ # @abstract
7
+ def self.for(path)
8
+ fail NotImplementedError
9
+ end
10
+
11
+ # @abstract
12
+ def self.type
13
+ fail NotImplementedError
14
+ end
15
+
16
+ # @abstract
17
+ def get(address)
18
+ fail NotImplementedError
19
+ end
20
+
21
+ # @abstract
22
+ def put(address, object)
23
+ fail NotImplementedError
24
+ end
25
+
26
+ # @abstract
27
+ def delete(address)
28
+ fail NotImplementedError
29
+ end
30
+
31
+ # @abstract
32
+ def exist?(address)
33
+ fail NotImplementedError
34
+ end
35
+
36
+ # @abstract
37
+ def keys
38
+ fail NotImplementedError
39
+ end
40
+
41
+ # @abstract
42
+ def stats
43
+ fail NotImplementedError
44
+ end
45
+
46
+ # @abstract
47
+ def sync
48
+ fail NotImplementedError
49
+ end
50
+
51
+ # @abstract
52
+ def clear
53
+ fail NotImplementedError
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,80 @@
1
+ require 'leveldb'
2
+ require 'json'
3
+
4
+ module Yoda
5
+ module Store
6
+ module Adapters
7
+ class LeveldbAdapter < Base
8
+ class << self
9
+ def for(path)
10
+ @pool ||= {}
11
+ @pool[path] || (@pool[path] = new(path))
12
+ end
13
+
14
+ def type
15
+ :leveldb
16
+ end
17
+ end
18
+
19
+ # @param path [String] represents the path to store db.
20
+ def initialize(path)
21
+ @path = path
22
+ @db = LevelDB::DB.new(path, compression: true)
23
+
24
+ at_exit { @db.closed? || @db.close }
25
+ end
26
+
27
+ # @param address [String]
28
+ # @return [any]
29
+ def get(address)
30
+ JSON.load(@db.get(address.to_s), symbolize_names: true)
31
+ end
32
+
33
+ # @param address [String]
34
+ # @param object [Object]
35
+ # @return [void]
36
+ def put(address, object)
37
+ @db.put(address.to_s, object.to_json)
38
+ end
39
+
40
+ # @param data [Enumerator<(String, Object)>]
41
+ # @param bar [ProgressBar, nil]
42
+ def batch_write(data, bar)
43
+ data.each do |(k, v)|
44
+ @db.put(k, v)
45
+ bar&.increment
46
+ end
47
+ end
48
+
49
+ # @param address [String]
50
+ # @return [void]
51
+ def delete(address)
52
+ @db.delete(address.to_s)
53
+ end
54
+
55
+ # @param address [String]
56
+ # @return [true, false]
57
+ def exist?(address)
58
+ @db.exists?(address.to_s)
59
+ end
60
+
61
+ # @return [Array<String>]
62
+ def keys
63
+ @db.keys
64
+ end
65
+
66
+ def stats
67
+ @db.stats
68
+ end
69
+
70
+ def clear
71
+ @db.destroy!
72
+ end
73
+
74
+ def sync
75
+ # nop
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end