sonic-ruby 0.2.1 → 0.2.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 481516faf364a56439e535dfe4bcfbadb957b2c736a1f1db0a2731ff9e949c45
4
- data.tar.gz: df5508697fbc38eb549b1bc9c9c9b4eba6c4cc62e5a59519e0cc44fe2fa9fa92
3
+ metadata.gz: 11f88ebf448e0353b9a260f54f223b9013bf184079f350e6ec9fe23782d3b274
4
+ data.tar.gz: 4522c74a64130e54a6990355999575f9e557fa51a59ae9b847a162acd182a8c4
5
5
  SHA512:
6
- metadata.gz: 2a572c3d807069ef41574e60a9ee6df12d8b5bbc2ffd6893e97659c2b1c1ec741b3fbd04e4031b8cc9654415553f150ed45781417c80a3fc1f980b73574b0489
7
- data.tar.gz: 4dd866ce47e4f63e2a520ad9bd39fa99a5b3d8e678366c1276be95b2543e7685fc2f67876b118223e84b4ece4dbfb935617a5f38f5d8a22623007ad1b4924a53
6
+ metadata.gz: b7fde18e4818984c29d07354706a907e04664eca736fe17bbe5584212532520009a41162cd86a7807cc1f316dd707fc34f44e4294439f231b1c8fef38c134ac7
7
+ data.tar.gz: 378de35eb10db30414aa16dc0c746b44b748c94ac74790ced01729c3ed4330eba245ff17b1f815644a935401ebcc84eeebc1e6a62e1982a3ba45d972f449b3e2
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,97 @@
1
+ # sonic-ruby
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/sonic-ruby.svg)](https://badge.fury.io/rb/sonic-ruby)
4
+ [![Build Status](https://travis-ci.com/atipugin/sonic-ruby.svg?branch=master)](https://travis-ci.com/atipugin/sonic-ruby)
5
+ [![Maintainability](https://api.codeclimate.com/v1/badges/dcbb9919a64c96fb629c/maintainability)](https://codeclimate.com/github/atipugin/sonic-ruby/maintainability)
6
+
7
+ A Ruby client for [Sonic search backend](https://github.com/valeriansaliou/sonic).
8
+
9
+ ## Installation
10
+
11
+ Add following line to your Gemfile:
12
+
13
+ ```ruby
14
+ gem 'sonic-ruby'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ ```shell
20
+ $ bundle
21
+ ```
22
+
23
+ Or install it system-wide:
24
+
25
+ ```shell
26
+ $ gem install sonic-ruby
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ Start with creating new client:
32
+
33
+ ```ruby
34
+ client = Sonic::Client.new('localhost', 1491, 'SecretPassword')
35
+ ```
36
+
37
+ Now you can use `#channel` method in order to connect to specific channels:
38
+
39
+ ```ruby
40
+ control = client.channel(:control)
41
+ ingest = client.channel(:ingest)
42
+ search = client.channel(:search)
43
+ ```
44
+
45
+ [Learn more about Sonic Channels](https://github.com/valeriansaliou/sonic/blob/master/PROTOCOL.md).
46
+
47
+ ## Indexing
48
+
49
+ ```ruby
50
+ # Init `ingest` channel
51
+ ingest = client.channel(:ingest)
52
+
53
+ # Add data to index
54
+ ingest.push('users', 'all', 1, 'Alexander Tipugin')
55
+ # => true
56
+
57
+ # Remove data from index
58
+ ingest.pop('users', 'all', 1, 'Alexander Tipugin')
59
+ # => 2
60
+
61
+ # Count collection/bucket/object items
62
+ ingest.count('users', 'all', 1)
63
+ # => 1
64
+
65
+ # Flush entire collection
66
+ ingest.flushc('users')
67
+ # => 1
68
+
69
+ # Flush entire bucket inside collection
70
+ ingest.flushb('users', 'all')
71
+ # => 1
72
+
73
+ # Flush specific object inside bucket
74
+ ingest.flusho('users', 'all', 1)
75
+ # => 2
76
+ ```
77
+
78
+ ## Searching
79
+
80
+ ```ruby
81
+ # Init `search` channel
82
+ search = client.channel(:search)
83
+
84
+ # Find indexed object by term
85
+ search.query('users', 'all', 'tipugin')
86
+ # => 1
87
+
88
+ # Auto-complete word
89
+ search.suggest('users', 'all', 'alex')
90
+ # => alexander
91
+ ```
92
+
93
+ ## TODO
94
+
95
+ - [ ] Take into account maximum buffer size.
96
+ - [ ] Consider using connection pool.
97
+ - [x] Return more meaningful responses from commands (i.e. bool `true` instead of string `OK`, int `1` instead of string `RESULT 1` etc).
@@ -0,0 +1,12 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'bundler/setup'
3
+ require 'rubocop/rake_task'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RuboCop::RakeTask.new(:rubocop) do |task|
7
+ task.patterns = %w[lib/**/*.rb]
8
+ end
9
+
10
+ RSpec::Core::RakeTask.new(:spec)
11
+
12
+ task default: :spec
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ require 'bundler/setup'
3
+ require 'pry'
4
+
5
+ require File.expand_path('../../lib/sonic', __FILE__)
6
+
7
+ Pry.start
@@ -0,0 +1 @@
1
+ require 'sonic'
@@ -0,0 +1,7 @@
1
+ require 'socket'
2
+
3
+ require 'sonic/version'
4
+ require 'sonic/client'
5
+ require 'sonic/channels'
6
+ require 'sonic/connection'
7
+ require 'sonic/errors'
@@ -0,0 +1,4 @@
1
+ require 'sonic/channels/base'
2
+ require 'sonic/channels/control'
3
+ require 'sonic/channels/ingest'
4
+ require 'sonic/channels/search'
@@ -0,0 +1,48 @@
1
+ module Sonic
2
+ module Channels
3
+ class Base
4
+ attr_reader :connection
5
+
6
+ def initialize(connection)
7
+ @connection = connection
8
+ end
9
+
10
+ def ping
11
+ execute('PING')
12
+ end
13
+
14
+ def help(manual)
15
+ execute('HELP', manual)
16
+ end
17
+
18
+ def quit
19
+ execute('QUIT')
20
+ connection.disconnect
21
+ end
22
+
23
+ private
24
+
25
+ def execute(*args)
26
+ connection.write(*args.join(' '))
27
+ yield if block_given?
28
+ type_cast_response(connection.read)
29
+ end
30
+
31
+ def quote(value)
32
+ "\"#{value}\""
33
+ end
34
+
35
+ def type_cast_response(value)
36
+ if value == 'OK'
37
+ true
38
+ elsif value.start_with?('RESULT ')
39
+ value.split(' ').last.to_i
40
+ elsif value.start_with?('EVENT ')
41
+ value.split(' ')[3..-1].join(' ')
42
+ else
43
+ value
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,9 @@
1
+ module Sonic
2
+ module Channels
3
+ class Control < Base
4
+ def trigger(action)
5
+ execute('TRIGGER', action)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,32 @@
1
+ module Sonic
2
+ module Channels
3
+ class Ingest < Base
4
+ def push(collection, bucket, object, text, lang = nil)
5
+ arr = [collection, bucket, object, quote(text)]
6
+ arr << "LANG(#{lang})" if lang
7
+
8
+ execute('PUSH', *arr)
9
+ end
10
+
11
+ def pop(collection, bucket, object, text)
12
+ execute('POP', collection, bucket, object, quote(text))
13
+ end
14
+
15
+ def count(collection, bucket = nil, object = nil)
16
+ execute('COUNT', *[collection, bucket, object].compact)
17
+ end
18
+
19
+ def flushc(collection)
20
+ execute('FLUSHC', collection)
21
+ end
22
+
23
+ def flushb(collection, bucket)
24
+ execute('FLUSHB', collection, bucket)
25
+ end
26
+
27
+ def flusho(collection, bucket, object)
28
+ execute('FLUSHO', collection, bucket, object)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,25 @@
1
+ module Sonic
2
+ module Channels
3
+ class Search < Base
4
+ def query(collection, bucket, terms, limit = nil, offset = nil, lang = nil) # rubocop:disable Metrics/ParameterLists, Metrics/LineLength
5
+ arr = [collection, bucket, quote(terms)]
6
+ arr << "LIMIT(#{limit})" if limit
7
+ arr << "OFFSET(#{offset})" if offset
8
+ arr << "LANG(#{lang})" if lang
9
+
10
+ execute('QUERY', *arr) do
11
+ connection.read # ...
12
+ end
13
+ end
14
+
15
+ def suggest(collection, bucket, word, limit = nil)
16
+ arr = [collection, bucket, quote(word)]
17
+ arr << "LIMIT(#{limit})" if limit
18
+
19
+ execute('SUGGEST', *arr) do
20
+ connection.read # ...
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,24 @@
1
+ module Sonic
2
+ class Client
3
+ def initialize(host, port, password = nil)
4
+ @host = host
5
+ @port = port
6
+ @password = password
7
+ end
8
+
9
+ def channel(type)
10
+ channel_class(type).new(Connection.connect(@host, @port, type, @password))
11
+ end
12
+
13
+ private
14
+
15
+ def channel_class(type)
16
+ case type.to_sym
17
+ when :control then Channels::Control
18
+ when :ingest then Channels::Ingest
19
+ when :search then Channels::Search
20
+ else raise ArgumentError, "`#{type}` channel type is not supported"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,42 @@
1
+ module Sonic
2
+ class Connection
3
+ def self.connect(*args)
4
+ connection = new(*args)
5
+ connection if connection.connect
6
+ end
7
+
8
+ def initialize(host, port, channel_type, password = nil)
9
+ @host = host
10
+ @port = port
11
+ @channel_type = channel_type
12
+ @password = password
13
+ end
14
+
15
+ def connect
16
+ read # ...
17
+ write(['START', @channel_type, @password].compact.join(' '))
18
+ read.start_with?('STARTED ')
19
+ end
20
+
21
+ def disconnect
22
+ socket.close
23
+ end
24
+
25
+ def read
26
+ data = socket.gets.chomp
27
+ raise ServerError, data if data.start_with?('ERR ')
28
+
29
+ data
30
+ end
31
+
32
+ def write(data)
33
+ socket.puts(data)
34
+ end
35
+
36
+ private
37
+
38
+ def socket
39
+ @socket ||= TCPSocket.open(@host, @port)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,4 @@
1
+ module Sonic
2
+ class Error < StandardError; end
3
+ class ServerError < Error; end
4
+ end
@@ -0,0 +1,3 @@
1
+ module Sonic
2
+ VERSION = '0.2.2'.freeze
3
+ end
@@ -0,0 +1,19 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ require 'sonic/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'sonic-ruby'
8
+ spec.version = Sonic::VERSION
9
+ spec.summary = 'Ruby client for Sonic'
10
+ spec.authors = ['Alexander Tipugin']
11
+ spec.files = `git ls-files -z`.split("\x0").reject { |f|
12
+ f.match(%r{^(test|spec|features)/})
13
+ } - ['.rubocop.yml', '.travis.yml', 'Gemfile.lock', '.gitignore', '.rspec']
14
+
15
+ spec.add_development_dependency 'pry'
16
+ spec.add_development_dependency 'rake'
17
+ spec.add_development_dependency 'rspec'
18
+ spec.add_development_dependency 'rubocop', '~> 0.66.0'
19
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sonic-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Tipugin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-01 00:00:00.000000000 Z
11
+ date: 2019-04-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -71,7 +71,23 @@ email:
71
71
  executables: []
72
72
  extensions: []
73
73
  extra_rdoc_files: []
74
- files: []
74
+ files:
75
+ - Gemfile
76
+ - README.md
77
+ - Rakefile
78
+ - bin/console
79
+ - lib/sonic-ruby.rb
80
+ - lib/sonic.rb
81
+ - lib/sonic/channels.rb
82
+ - lib/sonic/channels/base.rb
83
+ - lib/sonic/channels/control.rb
84
+ - lib/sonic/channels/ingest.rb
85
+ - lib/sonic/channels/search.rb
86
+ - lib/sonic/client.rb
87
+ - lib/sonic/connection.rb
88
+ - lib/sonic/errors.rb
89
+ - lib/sonic/version.rb
90
+ - sonic-ruby.gemspec
75
91
  homepage:
76
92
  licenses: []
77
93
  metadata: {}