zeiger 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c01cd920ba6af86d17bd79b07e648d5e1bb37ad2
4
+ data.tar.gz: d90ce8ed808e1853aad0175d6fa862d0b3ff2a29
5
+ SHA512:
6
+ metadata.gz: 5c24d14cba4ac6840aa2ec817c22a608bc98a476289643bdaa6b06fcd50649f82a189f45fdc5c14ef882a7cb815bdcd900c4f387d2d3baa2f4ffeb8edb90b223
7
+ data.tar.gz: 491a7131fc1f5b5ba29b207d31d2c5b232ed9307650a17aecd6d9383f101debefd6f7e5366c79a2e8e456e82f3d52b3fa6c799a7282b64b4ff5ab2bd683e9aff
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ *.gem
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+ .#*
data/.zeiger.yml ADDED
@@ -0,0 +1,9 @@
1
+ search:
2
+ - bin
3
+ - lib
4
+ ignore:
5
+ - .gz$
6
+ - .png$
7
+ - .gif$
8
+ - .zip$
9
+ - .jpg$
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in zeiger.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2017 conanite
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # Zeiger
2
+
3
+ Zeiger is the German word for "pointer", "indicator", "index", "locator". This gem is built in the "index" sense : run
4
+
5
+ ```
6
+ cd myproject
7
+ zeiger server
8
+ ```
9
+
10
+ and this gem will create an in-memory index of all text in the filesystem subtree rooted in the current directory.
11
+
12
+ Query the index thus:
13
+
14
+ ```
15
+ cd myproject
16
+ zeiger search "muppets"
17
+ ```
18
+
19
+ This example returns one line for each line in your projects containing the word "muppets". Output is in the same format as `grep` (so you can hook it up with your emacs for quick project browsing).
20
+
21
+ ## Installation
22
+
23
+ ```
24
+ gem install 'zeiger'
25
+ ```
26
+
27
+ This is built as a standalone commandline tool ; I don't have any use-cases for integrating it directly into a larger project. But if you do, I'm all ears.
28
+
29
+ ## Usage
30
+
31
+ `zeiger server` runs the server and opens a unix filesystem socket called `zeiger-index` in the current directory.
32
+
33
+ `zeiger search "foo"` writes the query to the socket and displays the result
34
+
35
+ `zeiger files "xed"` asks for the list of filenames corresponding to the argument ("xed"). With no argument, return all filenames.
36
+
37
+ By default, Zeiger searches only in these subdirectories : %w{ app bin config lib spec test }, and excludes filenames matching these patterns: %w{ .gz$ .png$ .jpg$ .pdf$ }.
38
+
39
+ To override, create a file in your working directory with the following format:
40
+
41
+ ```yaml
42
+ search:
43
+ - bin
44
+ - lib
45
+ - config.rb
46
+ ignore:
47
+ - .gz$
48
+ - .png$
49
+ - .gif$
50
+ - .zip$
51
+ - .jpg$
52
+ - .xcf$
53
+ - .mpg$
54
+ ```
55
+
56
+
57
+ ## Contributing
58
+
59
+ 1. Fork it ( https://github.com/conanite/zeiger/fork )
60
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
61
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
62
+ 4. Push to the branch (`git push origin my-new-feature`)
63
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/zeiger ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'socket'
4
+ require 'zeiger'
5
+
6
+ SOCKET_NAME = "./zeiger-index"
7
+
8
+ command = $*[0]
9
+
10
+ case command
11
+ when "server" ; Zeiger::Server.run *$*
12
+ when "search" ; Zeiger::QueryClient.run *$*
13
+ when "files" ; Zeiger::FileListClient.run *$*
14
+ end
15
+
16
+ # define file groups
17
+
18
+ # file_group :app, patterns: [/^app/ ]
19
+ # file_group :helpers, patterns: [/^app\/helpers/ ]
20
+ # file_group :spec, patterns: [/^spec/ ]
21
+ # file_group :sass, patterns: [/\.sass$/ ]
22
+ # file_group :all, groups: %w{app spec sass}
23
+
24
+
25
+ # writing to socket: nc -U /tmp/uss
@@ -0,0 +1,23 @@
1
+ require 'set'
2
+
3
+ module Zeiger
4
+ class FileInfo
5
+ attr_accessor :dir, :filename, :ngrams
6
+
7
+ def initialize dir, filename
8
+ @dir, @filename, @ngrams = dir, filename, Set.new
9
+ end
10
+
11
+ def local_filename
12
+ filename.gsub(/^#{Regexp.escape dir}\//, "")
13
+ end
14
+
15
+ def match regex
16
+ local_filename.match regex
17
+ end
18
+
19
+ def add_ngram ngram
20
+ @ngrams << ngram
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ module Zeiger
2
+ class FileListClient
3
+ def self.run command, q=nil, *args
4
+ Socket.unix(SOCKET_NAME) { |sock|
5
+ sock.puts("FILES: #{q}")
6
+ while !sock.eof?
7
+ puts sock.readline
8
+ end
9
+ }
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,50 @@
1
+ module Zeiger
2
+ class Index
3
+ NGRAM_SIZE = 3
4
+
5
+ attr_accessor :index, :dir, :includes, :ignore, :files
6
+
7
+ def initialize dir
8
+ attrs = File.exist?(".zeiger.yml") ? YAML.load(File.read ".zeiger.yml") : { }
9
+ self.dir = File.expand_path dir
10
+ self.index = Hash.new { |h, k| h[k] = [] }
11
+ self.files = Hash.new
12
+ end
13
+
14
+ def remove_from_index file
15
+ info = files[file]
16
+ if info
17
+ info.ngrams.each { |ngram|
18
+ index[ngram].reject! { |line| line.file == info }
19
+ }
20
+ end
21
+ files.delete file
22
+ end
23
+
24
+ def add_to_index file
25
+ info = files[file] = FileInfo.new(dir, file)
26
+
27
+ File.read(file).split(/\n/).each_with_index { |txt, line|
28
+ Line.new(info, line + 1, txt).ngrams(NGRAM_SIZE) do |trig, line|
29
+ index[trig] << line
30
+ info.add_ngram trig
31
+ end
32
+ }
33
+ end
34
+
35
+ def exec_query regex, ngrams
36
+ ngrams.map { |ngram| index[ngram] }.reduce(&:&).select { |line| line.matches? regex }
37
+ end
38
+
39
+ def query txt
40
+ puts "got query #{txt.inspect}"
41
+ exec_query Regexp.compile(txt), txt.ngrams(NGRAM_SIZE)
42
+ end
43
+
44
+ def file_list name
45
+ r = Regexp.compile name
46
+ puts "file names matching #{r.inspect}"
47
+ files.values.select { |f| f.match r }
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,17 @@
1
+ module Zeiger
2
+ class Line
3
+ attr_accessor :file, :line_number, :content
4
+ attr_reader :hash
5
+
6
+ def initialize file, line_number, content
7
+ @file, @line_number, @content = file, line_number, content
8
+ @hash = "#{file.filename}##{line_number}".hash
9
+ end
10
+
11
+ def to_s ; "#{file.local_filename}:#{line_number}:#{content}" ; end
12
+ def matches? regex ; content.match regex ; end
13
+ def ngram_list size ; @ngrams ||= content.ngrams(size) ; end
14
+ def ngrams size ; ngram_list(size).each { |ngram| yield ngram, self } ; end
15
+ def == other ; self.file == other.file && self.line_number == other.line_number ; end
16
+ end
17
+ end
@@ -0,0 +1,44 @@
1
+ module Zeiger
2
+ class Monitor
3
+ attr_accessor :dir, :index, :stat, :includes, :ignore
4
+
5
+ def initialize dir, index
6
+ @dir, @index, @stat = dir, index, Hash.new
7
+ attrs = File.exist?(".zeiger.yml") ? YAML.load(File.read ".zeiger.yml") : { }
8
+ @includes = attrs["includes"] || %w{ app bin config lib spec test }
9
+ @ignore = attrs["ignore"] || %w{ .gz$ .png$ .jpg$ .pdf$ }
10
+ end
11
+
12
+ def ignore? filename
13
+ ignore.any? { |ig| filename.match ig }
14
+ end
15
+
16
+ def uptodate? filename, mtime
17
+ stat.include?(filename) && stat[filename] == mtime
18
+ end
19
+
20
+ def build_index
21
+ started = Time.now
22
+ files = Set.new
23
+ includes.each do |inc|
24
+ Dir.glob(File.join(dir, inc, "**", "*")).sort.each do |file|
25
+ if File.file?(file) && !ignore?(file)
26
+ files << file
27
+ mtime = File.stat(file).mtime
28
+ if !uptodate?(file, mtime)
29
+ puts "re-indexing #{file}"
30
+ index.remove_from_index file
31
+ index.add_to_index file
32
+ stat[file] = mtime
33
+ end
34
+ end
35
+ end
36
+ end
37
+ (Set.new(stat.keys) - files).each { |f| index.remove_from_index f }
38
+ finished = Time.now
39
+ puts "ngrams : #{index.index.length}"
40
+ puts "files : #{index.files.length}"
41
+ puts "#build_index : #{(finished - started)} sec"
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,12 @@
1
+ module Zeiger
2
+ class QueryClient
3
+ def self.run command, q, *args
4
+ Socket.unix(SOCKET_NAME) { |sock|
5
+ sock.puts("SEARCH: #{q}")
6
+ while !sock.eof?
7
+ puts sock.readline
8
+ end
9
+ }
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,41 @@
1
+ module Zeiger
2
+ class Server
3
+ def self.run command, *args
4
+ dir = File.expand_path(".")
5
+ z = Zeiger::Index.new dir
6
+
7
+ Thread.new do
8
+ begin
9
+ puts "monitor thread"
10
+ monitor = Zeiger::Monitor.new dir, z
11
+ puts "created monitor"
12
+ while true do
13
+ puts "scanning..."
14
+ monitor.build_index
15
+ sleep 10
16
+ end
17
+ rescue Exception => e
18
+ puts e.message
19
+ puts e.backtrace
20
+ end
21
+ end
22
+
23
+ puts "query thread..."
24
+
25
+ Socket.unix_server_loop(SOCKET_NAME) { |sock, client|
26
+ puts "query thread: server loop"
27
+ begin
28
+ incoming = sock.readline.strip.split(/:/, 2).map &:strip
29
+ case incoming[0]
30
+ when "SEARCH"
31
+ z.query(incoming[1]).each { |res| sock.puts res.to_s }
32
+ when "FILES"
33
+ z.file_list(incoming[1]).each { |f| sock.puts f.local_filename }
34
+ end
35
+ ensure
36
+ sock.close
37
+ end
38
+ }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module Zeiger
2
+ VERSION = "0.0.1"
3
+ end
data/lib/zeiger.rb ADDED
@@ -0,0 +1,27 @@
1
+ require "set"
2
+ require 'yaml'
3
+ require 'zeiger/query_client'
4
+ require 'zeiger/file_list_client'
5
+ require 'zeiger/server'
6
+ require "zeiger/version"
7
+ require 'zeiger/monitor'
8
+ require 'zeiger/file_info'
9
+ require 'zeiger/index'
10
+ require 'zeiger/line'
11
+
12
+ class String
13
+ def ngrams size
14
+ padding = " " * (size - 1)
15
+ txt = self
16
+ ngrams = Hash.new { |h,k| h[k] = 0 }
17
+ regex = Regexp.compile("." * size)
18
+
19
+ result = []
20
+
21
+ (txt.length - 2).times do |i|
22
+ result << txt[i..(i + 2)]
23
+ end
24
+
25
+ result
26
+ end
27
+ end
data/zeiger.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'zeiger/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "zeiger"
8
+ spec.version = Zeiger::VERSION
9
+ spec.authors = ["conanite"]
10
+ spec.email = ["conan@conandalton.net"]
11
+ spec.summary = %q{Provide text index of files in current directory tree and a unix socket to query on}
12
+ spec.description = %q{Maintain an in-memory text index of files in current directory tree, allow querying via unix socket in current directory }
13
+ spec.homepage = "https://github.com/conanite/zeiger"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zeiger
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - conanite
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-05-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: 'Maintain an in-memory text index of files in current directory tree,
42
+ allow querying via unix socket in current directory '
43
+ email:
44
+ - conan@conandalton.net
45
+ executables:
46
+ - zeiger
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - ".gitignore"
51
+ - ".zeiger.yml"
52
+ - Gemfile
53
+ - LICENSE.txt
54
+ - README.md
55
+ - Rakefile
56
+ - bin/zeiger
57
+ - lib/zeiger.rb
58
+ - lib/zeiger/file_info.rb
59
+ - lib/zeiger/file_list_client.rb
60
+ - lib/zeiger/index.rb
61
+ - lib/zeiger/line.rb
62
+ - lib/zeiger/monitor.rb
63
+ - lib/zeiger/query_client.rb
64
+ - lib/zeiger/server.rb
65
+ - lib/zeiger/version.rb
66
+ - zeiger.gemspec
67
+ homepage: https://github.com/conanite/zeiger
68
+ licenses:
69
+ - MIT
70
+ metadata: {}
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubyforge_project:
87
+ rubygems_version: 2.2.2
88
+ signing_key:
89
+ specification_version: 4
90
+ summary: Provide text index of files in current directory tree and a unix socket to
91
+ query on
92
+ test_files: []