ion 0.0.1

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.
@@ -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