texticle 2.0.pre3 → 2.0.pre4
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/CHANGELOG.rdoc +20 -0
- data/Manifest.txt +6 -7
- data/README.rdoc +3 -0
- data/Rakefile +11 -0
- data/lib/texticle/searchable.rb +16 -0
- data/lib/texticle.rb +8 -8
- data/spec/texticle/searchable_spec.rb +57 -0
- data/spec/texticle_spec.rb +41 -32
- metadata +3 -1
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
== 2.0.pre4
|
2
|
+
|
3
|
+
* 1 new feature
|
4
|
+
|
5
|
+
* Searchable is now available to specify which columns you want searched:
|
6
|
+
|
7
|
+
require 'texticle/searchable'
|
8
|
+
class Game
|
9
|
+
include Searchable(:title)
|
10
|
+
end
|
11
|
+
|
12
|
+
This also allows Texticle use in Rails without having #search available to all models:
|
13
|
+
|
14
|
+
gem 'texticle', '~> 2.0.pre4', :require => 'texticle/searchable'
|
15
|
+
|
16
|
+
* 1 bugfix
|
17
|
+
|
18
|
+
* ActiveRecord::Base.extend(Texticle) doesn't break #method_missing and #respond_to? anymore
|
19
|
+
|
20
|
+
|
1
21
|
== 2.0.pre3
|
2
22
|
|
3
23
|
* 1 new feature
|
data/Manifest.txt
CHANGED
@@ -4,10 +4,9 @@ Manifest.txt
|
|
4
4
|
README.rdoc
|
5
5
|
Rakefile
|
6
6
|
lib/texticle.rb
|
7
|
-
lib/texticle/
|
8
|
-
lib/texticle/
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
test/test_texticle.rb
|
7
|
+
lib/texticle/rails.rb
|
8
|
+
lib/texticle/searchable.rb
|
9
|
+
spec/config.yml
|
10
|
+
spec/spec_helper.rb
|
11
|
+
spec/texticle_spec.rb
|
12
|
+
spec/texticle/searchable_spec.rb
|
data/README.rdoc
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
= texticle
|
2
2
|
|
3
|
+
Further documentation available at:
|
4
|
+
|
3
5
|
* http://tenderlove.github.com/texticle
|
4
6
|
|
5
7
|
== DESCRIPTION:
|
@@ -33,6 +35,7 @@ Your models now have access to the search method:
|
|
33
35
|
Game.search('Sonic') # will search through the model's :string columns
|
34
36
|
Game.search(:title => 'Mario')
|
35
37
|
Game.search_by_title('Street Fighter').search_by_system('PS3')
|
38
|
+
Game.search_by_title_and_system('Final Fantasy', 'PS2')
|
36
39
|
|
37
40
|
== REQUIREMENTS:
|
38
41
|
|
data/Rakefile
CHANGED
@@ -15,6 +15,17 @@ namespace :db do
|
|
15
15
|
table.string :system
|
16
16
|
table.string :title
|
17
17
|
end
|
18
|
+
create_table :web_comics do |table|
|
19
|
+
table.string :name
|
20
|
+
table.string :author
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
desc 'Drop tables from test database'
|
25
|
+
task :drop do
|
26
|
+
ActiveRecord::Migration.instance_eval do
|
27
|
+
drop_table :games
|
28
|
+
drop_table :web_comics
|
18
29
|
end
|
19
30
|
end
|
20
31
|
end
|
data/lib/texticle.rb
CHANGED
@@ -6,11 +6,10 @@ module Texticle
|
|
6
6
|
language = connection.quote('english')
|
7
7
|
|
8
8
|
exclusive = true
|
9
|
-
string_columns = columns.select {|column| column.type == :string }.map(&:name)
|
10
9
|
|
11
10
|
unless query.is_a?(Hash)
|
12
11
|
exclusive = false
|
13
|
-
query =
|
12
|
+
query = searchable_columns.inject({}) do |terms, column|
|
14
13
|
terms.merge column => query.to_s
|
15
14
|
end
|
16
15
|
end
|
@@ -18,11 +17,6 @@ module Texticle
|
|
18
17
|
similarities = []
|
19
18
|
conditions = []
|
20
19
|
|
21
|
-
select_values = scoped.select_values.map(&:to_s) & string_columns
|
22
|
-
query.select! do |column, search_term|
|
23
|
-
select_values.include? column
|
24
|
-
end unless select_values.empty?
|
25
|
-
|
26
20
|
query.each do |column, search_term|
|
27
21
|
column = connection.quote_column_name(column)
|
28
22
|
search_term = connection.quote normalize(Helper.normalize(search_term))
|
@@ -32,12 +26,13 @@ module Texticle
|
|
32
26
|
|
33
27
|
rank = connection.quote_column_name('rank' + rand.to_s)
|
34
28
|
|
35
|
-
select("#{quoted_table_name + '.*,' if select_values.empty?} #{similarities.join(" + ")} AS #{rank}").
|
29
|
+
select("#{quoted_table_name + '.*,' if scoped.select_values.empty?} #{similarities.join(" + ")} AS #{rank}").
|
36
30
|
where(conditions.join(exclusive ? " AND " : " OR ")).
|
37
31
|
order("#{rank} DESC")
|
38
32
|
end
|
39
33
|
|
40
34
|
def method_missing(method, *search_terms)
|
35
|
+
return super if self == ActiveRecord::Base
|
41
36
|
if Helper.dynamic_search_method?(method, self.columns)
|
42
37
|
columns = Helper.dynamic_search_columns(method)
|
43
38
|
metaclass = class << self; self; end
|
@@ -54,6 +49,7 @@ module Texticle
|
|
54
49
|
end
|
55
50
|
|
56
51
|
def respond_to?(method, include_private = false)
|
52
|
+
return super if self == ActiveRecord::Base
|
57
53
|
Helper.dynamic_search_method?(method, self.columns) ? true : super
|
58
54
|
end
|
59
55
|
|
@@ -63,6 +59,10 @@ module Texticle
|
|
63
59
|
query
|
64
60
|
end
|
65
61
|
|
62
|
+
def searchable_columns
|
63
|
+
columns.select {|column| column.type == :string }.map(&:name)
|
64
|
+
end
|
65
|
+
|
66
66
|
module Helper
|
67
67
|
class << self
|
68
68
|
def normalize(query)
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'texticle/searchable'
|
3
|
+
|
4
|
+
class WebComic < ActiveRecord::Base
|
5
|
+
# string :name
|
6
|
+
# string :author
|
7
|
+
end
|
8
|
+
|
9
|
+
class SearchableTest < Test::Unit::TestCase
|
10
|
+
|
11
|
+
context "when extending an ActiveRecord::Base subclass" do
|
12
|
+
setup do
|
13
|
+
@qcont = WebComic.create :name => "Questionable Content", :author => "Jeff Jaques"
|
14
|
+
@jhony = WebComic.create :name => "Johnny Wander", :author => "Ananth & Yuko"
|
15
|
+
@ddeeg = WebComic.create :name => "Dominic Deegan", :author => "Mookie"
|
16
|
+
@penny = WebComic.create :name => "Penny Arcade", :author => "Tycho & Gabe"
|
17
|
+
end
|
18
|
+
|
19
|
+
teardown do
|
20
|
+
WebComic.delete_all
|
21
|
+
end
|
22
|
+
|
23
|
+
context "with no paramters" do
|
24
|
+
setup do
|
25
|
+
WebComic.extend(Searchable)
|
26
|
+
end
|
27
|
+
|
28
|
+
should "search across all columns" do
|
29
|
+
assert_equal [@penny], WebComic.search("Penny")
|
30
|
+
assert_equal [@ddeeg], WebComic.search("Dominic")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "with one column as parameter" do
|
35
|
+
setup do
|
36
|
+
WebComic.extend(Searchable(:name))
|
37
|
+
end
|
38
|
+
|
39
|
+
should "only search across the given column" do
|
40
|
+
assert_equal [@penny], WebComic.search("Penny")
|
41
|
+
assert_empty WebComic.search("Tycho")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "with two columns as parameters" do
|
46
|
+
setup do
|
47
|
+
WebComic.extend(Searchable(:name, :author))
|
48
|
+
end
|
49
|
+
|
50
|
+
should "only search across the given column" do
|
51
|
+
assert_equal [@penny], WebComic.search("Penny")
|
52
|
+
assert_equal [@penny], WebComic.search("Tycho")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
data/spec/texticle_spec.rb
CHANGED
@@ -12,6 +12,26 @@ end
|
|
12
12
|
|
13
13
|
class TexticleTest < Test::Unit::TestCase
|
14
14
|
|
15
|
+
context "after extending ActiveRecord::Base" do
|
16
|
+
setup do
|
17
|
+
ActiveRecord::Base.extend(Texticle)
|
18
|
+
end
|
19
|
+
|
20
|
+
should "not break #respond_to?" do
|
21
|
+
assert_nothing_raised do
|
22
|
+
ActiveRecord::Base.respond_to? :abstract_class?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
should "not break #method_missing" do
|
27
|
+
begin
|
28
|
+
ActiveRecord::Base.random
|
29
|
+
rescue NoMethodError => error
|
30
|
+
assert_match error.message, /undefined method `random'/
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
15
35
|
context "after extending an ActiveRecord::Base subclass" do
|
16
36
|
setup do
|
17
37
|
Game.extend(Texticle)
|
@@ -35,70 +55,64 @@ class TexticleTest < Test::Unit::TestCase
|
|
35
55
|
|
36
56
|
context "when searching with a String argument" do
|
37
57
|
should "search across all :string columns if no indexes have been specified" do
|
38
|
-
assert_equal @mario, Game.search("Mario")
|
39
|
-
assert_equal
|
40
|
-
|
41
|
-
assert (Game.search("NES") && [@mario, @zelda]) == [@mario, @zelda]
|
42
|
-
assert_equal 2, Game.search("NES").count
|
58
|
+
assert_equal [@mario], Game.search("Mario")
|
59
|
+
assert_equal Set.new([@mario, @zelda]), Game.search("NES").to_set
|
43
60
|
end
|
44
61
|
|
45
62
|
should "work if the query contains an apostrophe" do
|
46
|
-
assert_equal @dkong, Game.search("Diddy's")
|
47
|
-
assert_equal 1, Game.search("Diddy's").count
|
63
|
+
assert_equal [@dkong], Game.search("Diddy's")
|
48
64
|
end
|
49
65
|
|
50
66
|
should "work if the query contains whitespace" do
|
51
|
-
assert_equal @megam, Game.search("Mega Man")
|
67
|
+
assert_equal [@megam], Game.search("Mega Man")
|
52
68
|
end
|
53
69
|
|
54
70
|
should "work if the query contains an accent" do
|
55
|
-
assert_equal @takun, Game.search("Tarurūto-kun")
|
71
|
+
assert_equal [@takun], Game.search("Tarurūto-kun")
|
56
72
|
end
|
57
73
|
|
58
74
|
should "search across records with NULL values" do
|
59
|
-
assert_equal @megam, Game.search("Mega")
|
75
|
+
assert_equal [@megam], Game.search("Mega")
|
60
76
|
end
|
61
77
|
|
62
78
|
should "scope consecutively" do
|
63
|
-
assert_equal @sfgen, Game.search("Genesis").search("Street Fighter")
|
79
|
+
assert_equal [@sfgen], Game.search("Genesis").search("Street Fighter")
|
64
80
|
end
|
65
81
|
end
|
66
82
|
|
67
83
|
context "when searching with a Hash argument" do
|
68
84
|
should "search across the given columns" do
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
assert Game.search(:system => "NES", :title => "Sonic").empty?
|
85
|
+
assert_empty Game.search(:title => "NES")
|
86
|
+
assert_empty Game.search(:system => "Mario")
|
87
|
+
assert_empty Game.search(:system => "NES", :title => "Sonic")
|
73
88
|
|
74
|
-
assert_equal @mario, Game.search(:title => "Mario")
|
75
|
-
assert_equal 1, Game.search(:title => "Mario").count
|
89
|
+
assert_equal [@mario], Game.search(:title => "Mario")
|
76
90
|
|
77
|
-
assert_equal 2,
|
91
|
+
assert_equal 2, Game.search(:system => "NES").count
|
78
92
|
|
79
|
-
assert_equal @zelda, Game.search(:system => "NES", :title => "Zelda")
|
80
|
-
assert_equal @megam, Game.search(:title => "Mega")
|
93
|
+
assert_equal [@zelda], Game.search(:system => "NES", :title => "Zelda")
|
94
|
+
assert_equal [@megam], Game.search(:title => "Mega")
|
81
95
|
end
|
82
96
|
|
83
97
|
should "scope consecutively" do
|
84
|
-
assert_equal @sfgen, Game.search(:system => "Genesis").search(:title => "Street Fighter")
|
98
|
+
assert_equal [@sfgen], Game.search(:system => "Genesis").search(:title => "Street Fighter")
|
85
99
|
end
|
86
100
|
end
|
87
101
|
|
88
102
|
context "when using dynamic search methods" do
|
89
103
|
should "generate methods for each :string column" do
|
90
|
-
assert_equal @mario, Game.search_by_title("Mario")
|
91
|
-
assert_equal @takun, Game.search_by_system("Saturn")
|
104
|
+
assert_equal [@mario], Game.search_by_title("Mario")
|
105
|
+
assert_equal [@takun], Game.search_by_system("Saturn")
|
92
106
|
end
|
93
107
|
|
94
108
|
should "generate methods for any combination of :string columns" do
|
95
|
-
assert_equal @mario, Game.search_by_title_and_system("Mario", "NES")
|
96
|
-
assert_equal @sonic, Game.search_by_system_and_title("Genesis", "Sonic")
|
97
|
-
assert_equal @mario, Game.search_by_title_and_title("Mario", "Mario")
|
109
|
+
assert_equal [@mario], Game.search_by_title_and_system("Mario", "NES")
|
110
|
+
assert_equal [@sonic], Game.search_by_system_and_title("Genesis", "Sonic")
|
111
|
+
assert_equal [@mario], Game.search_by_title_and_title("Mario", "Mario")
|
98
112
|
end
|
99
113
|
|
100
114
|
should "scope consecutively" do
|
101
|
-
assert_equal @sfgen, Game.search_by_system("Genesis").search_by_title("Street Fighter")
|
115
|
+
assert_equal [@sfgen], Game.search_by_system("Genesis").search_by_title("Street Fighter")
|
102
116
|
end
|
103
117
|
|
104
118
|
should "not generate methods for non-:string columns" do
|
@@ -120,11 +134,6 @@ class TexticleTest < Test::Unit::TestCase
|
|
120
134
|
end
|
121
135
|
|
122
136
|
context "when searching after selecting columns to return" do
|
123
|
-
should "limit the search to the selected columns" do
|
124
|
-
assert_empty Game.select(:system).search("Mario")
|
125
|
-
assert_equal @mario.title, Game.select(:title).search("Mario").first.title
|
126
|
-
end
|
127
|
-
|
128
137
|
should "not fetch extra columns" do
|
129
138
|
assert_raise(ActiveModel::MissingAttributeError) do
|
130
139
|
Game.select(:title).search("Mario").first.system
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: texticle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease: 4
|
5
|
-
version: 2.0.
|
5
|
+
version: 2.0.pre4
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- ecin
|
@@ -65,9 +65,11 @@ files:
|
|
65
65
|
- README.rdoc
|
66
66
|
- Rakefile
|
67
67
|
- lib/texticle.rb
|
68
|
+
- lib/texticle/searchable.rb
|
68
69
|
- lib/texticle/rails.rb
|
69
70
|
- spec/spec_helper.rb
|
70
71
|
- spec/texticle_spec.rb
|
72
|
+
- spec/texticle/searchable_spec.rb
|
71
73
|
- spec/config.yml
|
72
74
|
homepage: http://tenderlove.github.com/texticle
|
73
75
|
licenses: []
|