oedipus 0.0.1 → 0.0.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.
- data/README.md +2 -2
- data/ext/oedipus/oedipus.c +2 -2
- data/ext/oedipus/oedipus.h +2 -2
- data/lib/oedipus/rspec/test_harness.rb +152 -0
- data/lib/oedipus/version.rb +1 -1
- data/spec/integration/connection_spec.rb +12 -1
- data/spec/integration/index_spec.rb +14 -3
- data/spec/spec_helper.rb +0 -2
- metadata +8 -9
- data/spec/support/test_harness.rb +0 -164
data/README.md
CHANGED
@@ -10,14 +10,14 @@ ActiveRecord or DataMapper... though this will follow in separate gems.
|
|
10
10
|
Oedipus provides a level of abstraction in terms of the ease with which faceted
|
11
11
|
search may be implemented, while remaining light and simple.
|
12
12
|
|
13
|
-
Data structures are managed using core ruby data
|
13
|
+
Data structures are managed using core ruby data types (Array and Hash), ensuring
|
14
14
|
simplicity and flexibilty.
|
15
15
|
|
16
16
|
## Dependencies
|
17
17
|
|
18
18
|
* ruby (>= 1.9)
|
19
19
|
* sphinx (>= 2.0.2)
|
20
|
-
* mysql
|
20
|
+
* mysql client development libraries (>= 4.1)
|
21
21
|
|
22
22
|
The gem builds a small (tiny) native extension for interfacing with mysql, as
|
23
23
|
existing gems either did not support multi-queries, or were too flaky
|
data/ext/oedipus/oedipus.c
CHANGED
@@ -160,7 +160,7 @@ static VALUE odp_query(VALUE self, VALUE sql) {
|
|
160
160
|
|
161
161
|
/* -- Internal functions -- */
|
162
162
|
|
163
|
-
static void odp_raise(VALUE self, const char *msg) {
|
163
|
+
static void odp_raise(VALUE self, const char * msg) {
|
164
164
|
OdpMysql * conn;
|
165
165
|
|
166
166
|
Data_Get_Struct(self, OdpMysql, conn);
|
@@ -168,7 +168,7 @@ static void odp_raise(VALUE self, const char *msg) {
|
|
168
168
|
"%s. Error %u: %s", msg, mysql_errno(conn->ptr), mysql_error(conn->ptr));
|
169
169
|
}
|
170
170
|
|
171
|
-
static void odp_free(OdpMysql *conn) {
|
171
|
+
static void odp_free(OdpMysql * conn) {
|
172
172
|
if (conn->connected) {
|
173
173
|
mysql_close(conn->ptr);
|
174
174
|
}
|
data/ext/oedipus/oedipus.h
CHANGED
@@ -13,7 +13,7 @@
|
|
13
13
|
/*! Internal struct used to reference a mysql connection */
|
14
14
|
typedef struct {
|
15
15
|
/*! Boolean representing the connected state */
|
16
|
-
int
|
16
|
+
int connected;
|
17
17
|
/*! The actual pointer allocated by mysql_init() */
|
18
18
|
MYSQL * ptr;
|
19
19
|
} OdpMysql;
|
@@ -47,4 +47,4 @@ static VALUE odp_cast_value(MYSQL_FIELD f, char * v, unsigned long len);
|
|
47
47
|
static void odp_raise(VALUE self, const char *msg);
|
48
48
|
|
49
49
|
/*! Free memory allocated to mysql */
|
50
|
-
static void odp_free(OdpMysql *conn);
|
50
|
+
static void odp_free(OdpMysql * conn);
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
##
|
4
|
+
# Oedipus Sphinx 2 Search.
|
5
|
+
# Copyright © 2012 Chris Corbyn.
|
6
|
+
#
|
7
|
+
# See LICENSE file for details.
|
8
|
+
##
|
9
|
+
|
10
|
+
module Oedipus
|
11
|
+
module RSpec
|
12
|
+
# Mixed into RSpec suites to manage starting/stopping Sphinx and writing indexes.
|
13
|
+
module TestHarness
|
14
|
+
# Set the path to the searchd executable.
|
15
|
+
#
|
16
|
+
# The version of Sphinx must be >= 2.0.2.
|
17
|
+
#
|
18
|
+
# @param [String] path
|
19
|
+
# the absolute path to searchd
|
20
|
+
def set_searchd(path)
|
21
|
+
@searchd = path
|
22
|
+
end
|
23
|
+
|
24
|
+
# Set the path to a temporary directory for writing test data to.
|
25
|
+
#
|
26
|
+
# @param [String] path
|
27
|
+
# the path to a writable directory whose contents may be completely deleted
|
28
|
+
def set_data_dir(path)
|
29
|
+
@data_dir = path
|
30
|
+
end
|
31
|
+
|
32
|
+
# Ensure that the temporary data directories exist and are clean.
|
33
|
+
def prepare_data_dirs
|
34
|
+
Dir.mkdir("#{data_dir}/index") unless Dir.exist?("#{data_dir}/index")
|
35
|
+
Dir.mkdir("#{data_dir}/binlog") unless Dir.exist?("#{data_dir}/binlog")
|
36
|
+
|
37
|
+
clean_data_dirs
|
38
|
+
end
|
39
|
+
|
40
|
+
def empty_indexes
|
41
|
+
@conn ||= Oedipus::Connection.new(host: searchd_host[:host], port: searchd_host[:port])
|
42
|
+
|
43
|
+
@conn.query("SHOW TABLES").each do |idx|
|
44
|
+
@conn.query("SELECT id FROM #{idx['Index']}").each do |hash|
|
45
|
+
@conn.execute("DELETE FROM #{idx['Index']} WHERE id = #{hash['id']}")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Write the sphinx.conf file, using #index_definiton.
|
51
|
+
#
|
52
|
+
# Sphinx will listen on localhost port 9399.
|
53
|
+
#
|
54
|
+
# Any string returned from #index_definition will be used to define one or more indexes.
|
55
|
+
def write_sphinx_conf
|
56
|
+
File.open(searchd_config, "wb") do |f|
|
57
|
+
f <<
|
58
|
+
<<-CONF.gsub(/^ {10}/m, "")
|
59
|
+
##
|
60
|
+
# This file is automatically generated during tests
|
61
|
+
##
|
62
|
+
|
63
|
+
index posts_rt
|
64
|
+
{
|
65
|
+
type = rt
|
66
|
+
path = #{data_dir}/index/posts_rt
|
67
|
+
|
68
|
+
rt_field = title
|
69
|
+
rt_field = body
|
70
|
+
|
71
|
+
rt_attr_uint = user_id
|
72
|
+
rt_attr_uint = views
|
73
|
+
rt_attr_string = status
|
74
|
+
}
|
75
|
+
|
76
|
+
searchd
|
77
|
+
{
|
78
|
+
compat_sphinxql_magics = 0
|
79
|
+
|
80
|
+
max_matches = 2000
|
81
|
+
pid_file = #{data_dir}/searchd.pid
|
82
|
+
listen = #{searchd_host[:host]}:#{searchd_host[:port]}:mysql41
|
83
|
+
workers = threads
|
84
|
+
log = #{data_dir}/searchd.log
|
85
|
+
query_log = #{data_dir}/searchd.log
|
86
|
+
binlog_path = #{data_dir}/binlog
|
87
|
+
}
|
88
|
+
CONF
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Start the sphinx daemon in a child process and return the PID.
|
93
|
+
#
|
94
|
+
# Output is redirected to searchd.out and searchd.err in the data dir.
|
95
|
+
def start_searchd
|
96
|
+
prepare_data_dirs
|
97
|
+
write_sphinx_conf
|
98
|
+
|
99
|
+
@searchd_pid = Process.spawn(
|
100
|
+
searchd, "--console", "-c", searchd_config,
|
101
|
+
out: "#{data_dir}/searchd.out",
|
102
|
+
err: "#{data_dir}/searchd.err"
|
103
|
+
)
|
104
|
+
sleep 1
|
105
|
+
end
|
106
|
+
|
107
|
+
# Stop an already running sphinx daemon and wait for it to shutdown.
|
108
|
+
def stop_searchd
|
109
|
+
Process.kill("TERM", @searchd_pid) && Process.wait
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def clean_data_dirs
|
115
|
+
clean_files("#{data_dir}/index/**/*")
|
116
|
+
clean_files("#{data_dir}/binlog/**/*")
|
117
|
+
clean_files("#{data_dir}/searchd.pid")
|
118
|
+
clean_files("#{data_dir}/searchd.out")
|
119
|
+
clean_files("#{data_dir}/searchd.err")
|
120
|
+
clean_files("#{data_dir}/sphinx.conf")
|
121
|
+
end
|
122
|
+
|
123
|
+
def searchd_host
|
124
|
+
{ host: "127.0.0.1", port: 9399 }
|
125
|
+
end
|
126
|
+
|
127
|
+
def searchd_config
|
128
|
+
"#{data_dir}/sphinx.conf"
|
129
|
+
end
|
130
|
+
|
131
|
+
def clean_files(path)
|
132
|
+
Dir[path].each { |f| File.delete(f) unless File.directory?(f) }
|
133
|
+
end
|
134
|
+
|
135
|
+
def data_dir
|
136
|
+
unless @data_dir
|
137
|
+
raise "Path to data directory unknown: call #set_data_dir during test setup"
|
138
|
+
else
|
139
|
+
@data_dir
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def searchd
|
144
|
+
unless @searchd
|
145
|
+
raise "Path to searchd unknown, perhaps you need to set the SEARCHD environment variable"
|
146
|
+
else
|
147
|
+
@searchd
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
data/lib/oedipus/version.rb
CHANGED
@@ -8,9 +8,20 @@
|
|
8
8
|
##
|
9
9
|
|
10
10
|
require "spec_helper"
|
11
|
+
require "oedipus/rspec/test_harness"
|
11
12
|
|
12
13
|
describe Oedipus::Connection do
|
13
|
-
include Oedipus::TestHarness
|
14
|
+
include Oedipus::RSpec::TestHarness
|
15
|
+
|
16
|
+
before(:all) do
|
17
|
+
set_data_dir File.expand_path("../../data", __FILE__)
|
18
|
+
set_searchd ENV["SEARCHD"]
|
19
|
+
start_searchd
|
20
|
+
end
|
21
|
+
|
22
|
+
after(:all) { stop_searchd }
|
23
|
+
|
24
|
+
before(:each) { empty_indexes }
|
14
25
|
|
15
26
|
let(:conn) { Oedipus::Connection.new(searchd_host) }
|
16
27
|
|
@@ -8,12 +8,23 @@
|
|
8
8
|
##
|
9
9
|
|
10
10
|
require "spec_helper"
|
11
|
+
require "oedipus/rspec/test_harness"
|
11
12
|
|
12
13
|
describe Oedipus::Index do
|
13
|
-
include Oedipus::TestHarness
|
14
|
+
include Oedipus::RSpec::TestHarness
|
14
15
|
|
15
|
-
|
16
|
-
|
16
|
+
before(:all) do
|
17
|
+
set_data_dir File.expand_path("../../data", __FILE__)
|
18
|
+
set_searchd ENV["SEARCHD"]
|
19
|
+
start_searchd
|
20
|
+
end
|
21
|
+
|
22
|
+
after(:all) { stop_searchd }
|
23
|
+
|
24
|
+
before(:each) { empty_indexes }
|
25
|
+
|
26
|
+
let(:conn) { Oedipus::Connection.new(searchd_host) }
|
27
|
+
let(:index) { Oedipus::Index.new(:posts_rt, conn) }
|
17
28
|
|
18
29
|
describe "#insert" do
|
19
30
|
context "with valid data" do
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oedipus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2012-04-26 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &17523900 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *17523900
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rake-compiler
|
27
|
-
requirement: &
|
27
|
+
requirement: &17785080 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *17785080
|
36
36
|
description: ! "== Sphinx 2 Comes to Ruby\n\nOedipus brings full support for Sphinx
|
37
37
|
2 to Ruby:\n\n - real-time indexes (insert, replace, update, delete)\n - faceted
|
38
38
|
search (variations on a base query)\n - multi-queries (multiple queries executed
|
@@ -73,13 +73,13 @@ files:
|
|
73
73
|
- lib/oedipus/connection_error.rb
|
74
74
|
- lib/oedipus/index.rb
|
75
75
|
- lib/oedipus/query_builder.rb
|
76
|
+
- lib/oedipus/rspec/test_harness.rb
|
76
77
|
- lib/oedipus/version.rb
|
77
78
|
- oedipus.gemspec
|
78
79
|
- spec/data/.gitkeep
|
79
80
|
- spec/integration/connection_spec.rb
|
80
81
|
- spec/integration/index_spec.rb
|
81
82
|
- spec/spec_helper.rb
|
82
|
-
- spec/support/test_harness.rb
|
83
83
|
- spec/unit/comparison/between_spec.rb
|
84
84
|
- spec/unit/comparison/equal_spec.rb
|
85
85
|
- spec/unit/comparison/gt_spec.rb
|
@@ -108,7 +108,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
108
108
|
version: '0'
|
109
109
|
segments:
|
110
110
|
- 0
|
111
|
-
hash: -
|
111
|
+
hash: -2156223476035950462
|
112
112
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
113
|
none: false
|
114
114
|
requirements:
|
@@ -117,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
117
|
version: '0'
|
118
118
|
segments:
|
119
119
|
- 0
|
120
|
-
hash: -
|
120
|
+
hash: -2156223476035950462
|
121
121
|
requirements: []
|
122
122
|
rubyforge_project: oedipus
|
123
123
|
rubygems_version: 1.8.11
|
@@ -129,7 +129,6 @@ test_files:
|
|
129
129
|
- spec/integration/connection_spec.rb
|
130
130
|
- spec/integration/index_spec.rb
|
131
131
|
- spec/spec_helper.rb
|
132
|
-
- spec/support/test_harness.rb
|
133
132
|
- spec/unit/comparison/between_spec.rb
|
134
133
|
- spec/unit/comparison/equal_spec.rb
|
135
134
|
- spec/unit/comparison/gt_spec.rb
|
@@ -1,164 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
##
|
4
|
-
# Oedipus Sphinx 2 Search.
|
5
|
-
# Copyright © 2012 Chris Corbyn.
|
6
|
-
#
|
7
|
-
# See LICENSE file for details.
|
8
|
-
##
|
9
|
-
|
10
|
-
module Oedipus
|
11
|
-
# Mixed into RSpec suites to manage starting/stopping Sphinx and writing indexes.
|
12
|
-
module TestHarness
|
13
|
-
class << self
|
14
|
-
def included(base)
|
15
|
-
base.before(:all) do
|
16
|
-
unless ENV.key?("SEARCHD")
|
17
|
-
raise "You must specify a path to the Sphinx 'searchd' executable (>= 2.0.2)"
|
18
|
-
end
|
19
|
-
|
20
|
-
set_data_dir File.expand_path("../../data", __FILE__)
|
21
|
-
set_searchd ENV["SEARCHD"]
|
22
|
-
|
23
|
-
prepare_data_dirs
|
24
|
-
write_sphinx_conf
|
25
|
-
start_searchd
|
26
|
-
end
|
27
|
-
|
28
|
-
base.before(:each) do
|
29
|
-
empty_indexes
|
30
|
-
end
|
31
|
-
|
32
|
-
base.after(:all) do
|
33
|
-
stop_searchd
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# Set the path to the searchd executable.
|
39
|
-
#
|
40
|
-
# The version of Sphinx must be >= 2.0.2.
|
41
|
-
#
|
42
|
-
# @param [String] path
|
43
|
-
# the absolute path to searchd
|
44
|
-
def set_searchd(path)
|
45
|
-
@searchd = path
|
46
|
-
end
|
47
|
-
|
48
|
-
# Set the path to a temporary directory for writing test data to.
|
49
|
-
#
|
50
|
-
# @param [String] path
|
51
|
-
# the path to a writable directory whose contents may be completely deleted
|
52
|
-
def set_data_dir(path)
|
53
|
-
@data_dir = path
|
54
|
-
end
|
55
|
-
|
56
|
-
# Ensure that the temporary data directories exist and are clean.
|
57
|
-
def prepare_data_dirs
|
58
|
-
Dir.mkdir("#{data_dir}/index") unless Dir.exist?("#{data_dir}/index")
|
59
|
-
Dir.mkdir("#{data_dir}/binlog") unless Dir.exist?("#{data_dir}/binlog")
|
60
|
-
|
61
|
-
clean_data_dirs
|
62
|
-
end
|
63
|
-
|
64
|
-
def empty_indexes
|
65
|
-
@conn ||= Oedipus::Connection.new(host: searchd_host[:host], port: searchd_host[:port])
|
66
|
-
|
67
|
-
@conn.query("SHOW TABLES").each do |idx|
|
68
|
-
@conn.query("SELECT id FROM #{idx['Index']}").each do |hash|
|
69
|
-
@conn.execute("DELETE FROM #{idx['Index']} WHERE id = #{hash['id']}")
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
# Write the sphinx.conf file, using #index_definiton.
|
75
|
-
#
|
76
|
-
# Sphinx will listen on localhost port 9399.
|
77
|
-
#
|
78
|
-
# Any string returned from #index_definition will be used to define one or more indexes.
|
79
|
-
def write_sphinx_conf
|
80
|
-
File.open(searchd_config, "wb") do |f|
|
81
|
-
f <<
|
82
|
-
<<-CONF.gsub(/^ {10}/m, "")
|
83
|
-
##
|
84
|
-
# This file is automatically generated during tests
|
85
|
-
##
|
86
|
-
|
87
|
-
index posts_rt
|
88
|
-
{
|
89
|
-
type = rt
|
90
|
-
path = #{data_dir}/index/posts_rt
|
91
|
-
|
92
|
-
rt_field = title
|
93
|
-
rt_field = body
|
94
|
-
|
95
|
-
rt_attr_uint = user_id
|
96
|
-
rt_attr_uint = views
|
97
|
-
rt_attr_string = status
|
98
|
-
}
|
99
|
-
|
100
|
-
searchd
|
101
|
-
{
|
102
|
-
compat_sphinxql_magics = 0
|
103
|
-
|
104
|
-
max_matches = 2000
|
105
|
-
pid_file = #{data_dir}/searchd.pid
|
106
|
-
listen = #{searchd_host[:host]}:#{searchd_host[:port]}:mysql41
|
107
|
-
workers = threads
|
108
|
-
log = #{data_dir}/searchd.log
|
109
|
-
query_log = #{data_dir}/searchd.log
|
110
|
-
binlog_path = #{data_dir}/binlog
|
111
|
-
}
|
112
|
-
CONF
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
# Start the sphinx daemon in a child process and return the PID.
|
117
|
-
#
|
118
|
-
# Output is redirected to searchd.out and searchd.err in the data dir.
|
119
|
-
def start_searchd
|
120
|
-
@searchd_pid = Process.spawn(
|
121
|
-
searchd, "--console", "-c", searchd_config,
|
122
|
-
out: "#{data_dir}/searchd.out",
|
123
|
-
err: "#{data_dir}/searchd.err"
|
124
|
-
)
|
125
|
-
sleep 1
|
126
|
-
end
|
127
|
-
|
128
|
-
# Stop an already running sphinx daemon and wait for it to shutdown.
|
129
|
-
def stop_searchd
|
130
|
-
Process.kill("TERM", @searchd_pid) && Process.wait
|
131
|
-
end
|
132
|
-
|
133
|
-
private
|
134
|
-
|
135
|
-
def clean_data_dirs
|
136
|
-
clean_files("#{data_dir}/index/**/*")
|
137
|
-
clean_files("#{data_dir}/binlog/**/*")
|
138
|
-
clean_files("#{data_dir}/searchd.pid")
|
139
|
-
clean_files("#{data_dir}/searchd.out")
|
140
|
-
clean_files("#{data_dir}/searchd.err")
|
141
|
-
clean_files("#{data_dir}/sphinx.conf")
|
142
|
-
end
|
143
|
-
|
144
|
-
def searchd_host
|
145
|
-
{ host: "127.0.0.1", port: 9399 }
|
146
|
-
end
|
147
|
-
|
148
|
-
def searchd
|
149
|
-
@searchd ||= "searchd"
|
150
|
-
end
|
151
|
-
|
152
|
-
def searchd_config
|
153
|
-
"#{data_dir}/sphinx.conf"
|
154
|
-
end
|
155
|
-
|
156
|
-
def clean_files(path)
|
157
|
-
Dir[path].each { |f| File.delete(f) unless File.directory?(f) }
|
158
|
-
end
|
159
|
-
|
160
|
-
def data_dir
|
161
|
-
@data_dir ||= "./data"
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|