ion 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ module Ion::Stringer
2
+ def self.sanitize(str)
3
+ return '' unless str.is_a?(String)
4
+ str.downcase.force_encoding('UTF-8')
5
+ end
6
+
7
+ # "Hey, yes you." => %w(hey yes you)
8
+ def self.keywords(str)
9
+ return Array.new unless str.is_a?(String)
10
+ self.sanitize(str).scan(/\w+/)
11
+ end
12
+
13
+ def self.classify(name)
14
+ str = name.to_s
15
+ str.scan(/\b(\w)(\w*)/).map { |(w, ord)| w.upcase + ord.downcase }.join('')
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ class Nest
2
+ def zlist
3
+ Hash[*zrange(0, -1, with_scores: true)]
4
+ end
5
+ end
6
+
7
+ def k
8
+ Ion.key
9
+ end
@@ -0,0 +1,31 @@
1
+ require 'yaml'
2
+
3
+ # Overrides `p` to be pretty -- useful for tests.
4
+ module Kernel
5
+ def p(*args)
6
+ # Catch source
7
+ begin
8
+ raise Error
9
+ rescue => e
10
+ file, line, _ = e.backtrace[2].split(':')
11
+ source = "%s:%s" % [ File.basename(file), line ]
12
+ end
13
+
14
+ # Source format
15
+ pre = "\033[0;33m%20s |\033[0;m "
16
+ lines = Array.new
17
+
18
+ # YAMLify the last arg if it's YAMLable
19
+ if args.last.is_a?(Hash) || args.last.is_a?(Array)
20
+ lines = args.pop.to_yaml.split("\n")[1..-1].map { |s| " #{s}" }
21
+ end
22
+
23
+ # Inspect everything else
24
+ lines.unshift(args.map { |a| a.is_a?(String) ? a : a.inspect }.join(' ')) if args.any?
25
+
26
+ # Print
27
+ print "\n"
28
+ puts pre % [source] + (lines.shift || "nil")
29
+ puts lines.map { |s| pre % [''] + s }.join("\n") if lines.any?
30
+ end
31
+ end
@@ -0,0 +1,8 @@
1
+ # Redis debug
2
+ class Redis::Client
3
+ def call(*args)
4
+ puts "REDIS:" + args.inspect.join(' ')
5
+ process(args) { read }
6
+ end
7
+ end
8
+
@@ -0,0 +1,81 @@
1
+ $:.unshift File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+
3
+ require 'ohm'
4
+ require 'ohm/contrib'
5
+ require 'ion'
6
+ require 'contest'
7
+ require_relative './p_helper'
8
+ #require_relative './redis_debug'
9
+
10
+ Ion.connect url: (ENV['REDIS_URL'] || 'redis://127.0.0.1:6379/0')
11
+
12
+ class Test::Unit::TestCase
13
+ def setup
14
+ re = Redis.current
15
+ keys = re.keys("Ion:*") + re.keys("IT::*")
16
+ re.del(*keys) if keys.any?
17
+ end
18
+
19
+ def redis
20
+ Ion.redis
21
+ end
22
+
23
+ def scores_for(search)
24
+ Hash[*search.key.zrange(0, -1, with_scores: true)]
25
+ end
26
+
27
+ def lorem
28
+ (0..5).map { lorem_words[(lorem_words.length * rand).to_i] }.join(' ')
29
+ end
30
+
31
+ def lorem_words
32
+ @w ||=
33
+ %w(lorem ipsum dolor sit amet consecteteur adicicising elit sed do eiusmod) +
34
+ %w(tempor incidudunt nam posture magna aliqua ut labore et dolore) +
35
+ %w(cum sociis nostrud aequitas verificium)
36
+ end
37
+
38
+ def ids(keys)
39
+ keys.map { |k| @items[k.to_sym].id }
40
+ end
41
+
42
+ def assert_ids(keys, args={})
43
+ if args[:ordered]
44
+ assert_equal ids(keys), args[:for].ids
45
+ else
46
+ assert_equal ids(keys).sort, args[:for].ids.sort
47
+ end
48
+ end
49
+
50
+ def assert_score(hash)
51
+ hash.each do |key, score|
52
+ assert_equal score.to_f, @scores[@items[key.to_sym].id].to_f
53
+ end
54
+ end
55
+ end
56
+
57
+ module IT
58
+ end
59
+
60
+ class IT::Album < Ohm::Model
61
+ include Ion::Entity
62
+ include Ohm::Callbacks
63
+
64
+ attribute :title
65
+ attribute :body
66
+ attribute :play_count
67
+
68
+ ion {
69
+ text :title
70
+ text :body
71
+ text(:also_title) { self.title }
72
+ number :play_count
73
+ sort :title
74
+ }
75
+
76
+ after :save, :update_ion_indices
77
+ before :delete, :delete_ion_indices
78
+ end
79
+
80
+ Album = IT::Album
81
+
@@ -0,0 +1,28 @@
1
+ require_relative '../test_helper'
2
+
3
+ class BoostTest < Test::Unit::TestCase
4
+ setup do
5
+ # Fake entries that should NOT be returned
6
+ 5.times { Album.create title: lorem, body: '' }
7
+
8
+ @items = {
9
+ :a => Album.create(title: "Morning Scifi"),
10
+ :b => Album.create(title: "Intensify", body: 'Special Edition'),
11
+ :c => Album.create(title: "BT Emotional Technology", body: 'Special Edition'),
12
+ :d => Album.create(title: "BT Movement in Still Life")
13
+ }
14
+ end
15
+
16
+ test "scores" do
17
+ search = Album.ion.search {
18
+ text :title, "bt"
19
+ boost(2.5) { text :body, "special edition" }
20
+ }
21
+
22
+ assert_ids %w(c d), for: search, ordered: true
23
+
24
+ @scores = scores_for search
25
+ assert_score c: 3.5
26
+ assert_score d: 1.0
27
+ end
28
+ end
@@ -0,0 +1,25 @@
1
+ require_relative '../test_helper'
2
+
3
+ class ConfigTest < Test::Unit::TestCase
4
+ setup do
5
+ # Mock erasing the config
6
+ Ion.instance_variable_set :@config, nil
7
+ @config = Ion.config
8
+ end
9
+
10
+ test "config" do
11
+ assert @config.is_a?(Ion::Config)
12
+ end
13
+
14
+ test "ignored words" do
15
+ assert @config.ignored_words.include?('a')
16
+ end
17
+
18
+ test "question mark" do
19
+ assert @config.ignored_words?
20
+
21
+ assert ! @config.foobar?
22
+ @config.foobar = 2
23
+ assert @config.foobar?
24
+ end
25
+ end
@@ -0,0 +1,48 @@
1
+ require_relative '../test_helper'
2
+
3
+ class HashTest < Test::Unit::TestCase
4
+ setup do
5
+ @search1 = Album.ion.search {
6
+ text :title, "Vloop"
7
+ text :body, "Secher betrib"
8
+ any_of {
9
+ text :body, "betrib"
10
+ text :body, "betrib"
11
+ any_of {
12
+ text :body, "betrib"
13
+ text :body, "betrib"
14
+ }
15
+ }
16
+ }
17
+
18
+ @search2 = Album.ion.search {
19
+ text :title, "Vloop"
20
+ text :body, "Secher betrib"
21
+ any_of {
22
+ text :body, "betrib"
23
+ text :body, "betrib"
24
+ any_of {
25
+ text :body, "betrib"
26
+ text :body, "betrib"
27
+ }
28
+ }
29
+ }
30
+
31
+ @search3 = Album.ion.search {
32
+ text :title, "Vloop"
33
+ text :body, "Secher betrib"
34
+ any_of {
35
+ text :body, "betrib"
36
+ text :body, "betrib"
37
+ any_of {
38
+ text :body, "betrib"
39
+ }
40
+ }
41
+ }
42
+ end
43
+
44
+ test "hash test" do
45
+ assert_equal @search1.search_hash, @search2.search_hash
46
+ assert @search3.search_hash != @search2.search_hash
47
+ end
48
+ end
@@ -0,0 +1,112 @@
1
+ require_relative '../test_helper'
2
+
3
+ class IonTest < Test::Unit::TestCase
4
+ setup do
5
+ # Fake entries that should NOT be returned
6
+ 5.times { Album.create title: lorem, body: '' }
7
+ end
8
+
9
+ test "single result" do
10
+ item = Album.create title: "First Lady of Song", body: "Ella Fitzgerald"
11
+ search = Album.ion.search { text :title, "lady" }
12
+
13
+ assert_equal [item.id], search.ids
14
+ end
15
+
16
+ test "lambda index" do
17
+ item = Album.create title: "First Lady of Song", body: "Ella Fitzgerald"
18
+ search = Album.ion.search { text :also_title, "lady" }
19
+
20
+ assert_equal [item.id], search.ids
21
+ end
22
+
23
+ test "stripping punctuations" do
24
+ item = Album.create title: "Leapin-and-Lopin'"
25
+ search = Album.ion.search { text :title, "leapin lopin" }
26
+
27
+ assert_equal [item.id], search.ids
28
+ end
29
+
30
+ test "many results" do
31
+ albums = (0..10).map {
32
+ Album.create title: "Hi #{lorem}"
33
+ }
34
+
35
+ search = Album.ion.search { text :title, "Hi" }
36
+
37
+ assert_equal albums.size, search.size
38
+ assert_equal albums.map(&:id).sort, search.ids.sort
39
+ end
40
+
41
+ test "multi keywords" do
42
+ album = Album.create title: "Mingus at the Bohemia"
43
+ search = Album.ion.search { text :title, "mingus bohemia" }
44
+
45
+ assert_equal [album.id], search.ids.sort
46
+ end
47
+
48
+ test "multi keywords fail" do
49
+ album = Album.create title: "Mingus at the Bohemia"
50
+ search = Album.ion.search { text :title, "bohemia is it" }
51
+
52
+ assert_equal [], search.ids.sort
53
+ end
54
+
55
+ test "search with arity" do
56
+ item = Album.create title: "Maiden Voyage"
57
+ search = Album.ion.search { |q| q.text :title, "maiden voyage" }
58
+
59
+ assert_equal [item.id], search.ids.sort
60
+ end
61
+
62
+ test "search within one index only" do
63
+ album1 = Album.create title: "Future 2 Future", body: "Herbie Hancock"
64
+ album2 = Album.create title: "Best of Herbie", body: "VA"
65
+
66
+ search = Album.ion.search { text :title, "herbie" }
67
+
68
+ assert_equal [album2.id], search.ids.sort
69
+ end
70
+
71
+ test "count" do
72
+ 5.times { Album.create(title: "Bebel Gilberto #{lorem}") }
73
+
74
+ search = Album.ion.search { text :title, "Bebel Gilberto" }
75
+ assert_equal 5, search.count
76
+ end
77
+
78
+ test "scores" do
79
+ @items = {
80
+ a: Album.create(title: "Future 2 Future", body: "Herbie Hancock"),
81
+ b: Album.create(title: "Best of Herbie", body: "Herbie Hancock")
82
+ }
83
+
84
+ search = Album.ion.search {
85
+ any_of {
86
+ text :title, "herbie"
87
+ text :body, "herbie"
88
+ }
89
+ }
90
+
91
+ # Album2 will go first because it matches both
92
+ assert_ids %w(a b), for: search
93
+
94
+ # Check if the scores are right
95
+ @scores = scores_for search
96
+ assert_score b: 2.0
97
+ assert_score a: 1.0
98
+ end
99
+
100
+ test "right scoring" do
101
+ @items = {
102
+ a: Album.create(title: "Best of Herbie", body: "Herbie Hancock")
103
+ }
104
+
105
+ search = Album.ion.search {
106
+ text :body, "Herbie Hancock"
107
+ }
108
+
109
+ @scores = scores_for search
110
+ assert_score a: 1.0
111
+ end
112
+ end
@@ -0,0 +1,37 @@
1
+ require_relative '../test_helper'
2
+
3
+ class IT::Song < Ohm::Model
4
+ include Ion::Entity
5
+ include Ohm::Callbacks
6
+
7
+ attribute :title
8
+ attribute :body
9
+
10
+ ion {
11
+ text :title
12
+ metaphone :body
13
+ }
14
+
15
+ after :save, :update_ion_indices
16
+ end
17
+
18
+ Song = IT::Song
19
+
20
+ class MetaphoneTest < Test::Unit::TestCase
21
+ setup do
22
+ # Fake entries that should NOT be returned
23
+ 5.times { Song.create title: lorem, body: '' }
24
+
25
+ @items = {
26
+ a: Song.create(body: "Stephanie"),
27
+ b: Song.create(body: "Ztephanno..!")
28
+ }
29
+ end
30
+
31
+ test "metaphone" do
32
+ search = Song.ion.search { metaphone :body, "Stefan" }
33
+
34
+ assert_ids %w(a b), for: search
35
+ end
36
+
37
+ end
@@ -0,0 +1,46 @@
1
+ require_relative '../test_helper'
2
+
3
+ class Number < Test::Unit::TestCase
4
+ setup do
5
+ @items = {
6
+ :a => Album.create(play_count: 1),
7
+ :b => Album.create(play_count: 2),
8
+ :c => Album.create(play_count: 3),
9
+ :d => Album.create(play_count: 4),
10
+ :e => Album.create(play_count: 4),
11
+ :f => Album.create(play_count: 4),
12
+ :g => Album.create(play_count: 5),
13
+ :h => Album.create(play_count: 5)
14
+ }
15
+ end
16
+
17
+ test "numb3rs" do
18
+ search = Album.ion.search { number :play_count, 4 }
19
+ assert_ids %w(d e f), for: search
20
+ end
21
+
22
+ test "greater than" do
23
+ search = Album.ion.search { number :play_count, gt: 3 }
24
+ assert_ids %w(d e f g h), for: search
25
+ end
26
+
27
+ test "greater than or equal" do
28
+ search = Album.ion.search { number :play_count, min: 3 }
29
+ assert_ids %w(c d e f g h), for: search
30
+ end
31
+
32
+ test "less than or equal" do
33
+ search = Album.ion.search { number :play_count, max: 3 }
34
+ assert_ids %w(a b c), for: search
35
+ end
36
+
37
+ test "greater than and less than" do
38
+ search = Album.ion.search { number :play_count, lt: 5, gt: 1 }
39
+ assert_ids %w(b c d e f), for: search
40
+ end
41
+
42
+ test "greater than equal and less than equal" do
43
+ search = Album.ion.search { number :play_count, min: 1, max: 3 }
44
+ assert_ids %w(a b c), for: search
45
+ end
46
+ end