rails-simple-search 1.1.9 → 1.2.0
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 +4 -4
- data/README.md +129 -0
- data/lib/rails-simple-search.rb +30 -41
- data/lib/sql_handler.rb +65 -60
- metadata +3 -3
- data/README.rdoc +0 -95
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 290634cd836385acaa327ac5b5971e75a716291d51629d8c54dc3c8db728e466
|
4
|
+
data.tar.gz: 9a8c7d6985147d3669c9cf1d98cf41fed6854e1039bdddf7fc271b6b4c4d5f01
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 890be5b99ab922d000adad63885a50a4bd678a1294b4915c32db93d808ac89774934626a0b73b3dd117728beb7d6b62a2df15c3b9353fb0113a80a05f11a57fa
|
7
|
+
data.tar.gz: 2e72fd3aef31f0b0b3fdf7b34e80a5f1035cac36b869a62d543d178c5bee132a78caf00605bd1bc93e60e675b52ced76a9fffffb0d22be23acad1e65adf0269b
|
data/README.md
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
## What is rails-simple-search?
|
2
|
+
rails-simple-search is a Ruby gem. It can help Ruby on Rails developers **quickly**
|
3
|
+
implement searching/filtering function against database. If you're not looking
|
4
|
+
for a full-text searching solution, this plugin will most probably **satisfy all**
|
5
|
+
your searching requirement.
|
6
|
+
|
7
|
+
## Why?
|
8
|
+
From time to time, I need to build pages to show a list of narrowed down records
|
9
|
+
from a database table by giving some searching criteria on some columns of the
|
10
|
+
table and/or of some referencing tables. Before I implemented this plugin, I usually
|
11
|
+
do the searching in the following way:
|
12
|
+
|
13
|
+
1. Use <%= form_tag %> to build a form in the view
|
14
|
+
2. Get the searching criteria from the params hash individually in the controller
|
15
|
+
and put them into instance variables to be used in view
|
16
|
+
3. Build the SQL WHERE clause and sometimes the JOIN clause according to the
|
17
|
+
values from the form
|
18
|
+
4. Run the find(:all, :conditions => [xxxxxxxx], :joins => "xxxxxxxx") with the
|
19
|
+
WHERE and JOIN clauses
|
20
|
+
|
21
|
+
After having used this pattern a few times, I realized I could DRY it to make
|
22
|
+
future coding of this kind of searching **much simpler**. That's where the
|
23
|
+
rails-simple-search plugin comes in.
|
24
|
+
|
25
|
+
## Installation
|
26
|
+
1. Put the following line into the Gemfile of your project
|
27
|
+
```
|
28
|
+
gem 'rails-simple-search'
|
29
|
+
```
|
30
|
+
|
31
|
+
2. Install the gems in Gemfile
|
32
|
+
```
|
33
|
+
$ bundle install
|
34
|
+
```
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
Let's suppose we have models of User, Address, Post and Comment. User model has_one
|
39
|
+
address, has_many posts, and has_many comments; Post model has_many comments; Comment
|
40
|
+
model belongs_to an author(of model User). We'd like to search for users according
|
41
|
+
to any combination of the following criteria:
|
42
|
+
|
43
|
+
* part of the user's email addrsss
|
44
|
+
* part of the first name or last name
|
45
|
+
* range of the user's birth date
|
46
|
+
* part of the city name of the user's address
|
47
|
+
* state of the user's address
|
48
|
+
* part of the name of any authors who commented the user's any posts
|
49
|
+
|
50
|
+
The following is how we implement this searching function with rails-simple-search:
|
51
|
+
|
52
|
+
1. Include gem into Gemfile
|
53
|
+
```
|
54
|
+
gem 'rails-simple-search'
|
55
|
+
```
|
56
|
+
|
57
|
+
2. Code in model (app/model/search.rb):
|
58
|
+
```
|
59
|
+
class Search < RailsSimpleSearch::Base
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
3. Code in controller:
|
64
|
+
```
|
65
|
+
@search = Search.new(User, params[:search])
|
66
|
+
@users = @search.run.order('email')
|
67
|
+
```
|
68
|
+
|
69
|
+
4. Code in views:
|
70
|
+
```
|
71
|
+
<% form_for @search, url => "/xxxxxx", data: {turbo: false} do |f| %>
|
72
|
+
|
73
|
+
<%=f.label :email %>
|
74
|
+
<%=f.text_field :email %>
|
75
|
+
|
76
|
+
<%=f.label "first name or last name" %>
|
77
|
+
<%=f.text_field "first_name_or_last_name" %>
|
78
|
+
|
79
|
+
<%=f.label :from_birth_date %>
|
80
|
+
<%=f.text_field :birth_date_greater_than_equal_to %>
|
81
|
+
|
82
|
+
<%=f.label :to_birth_date %>
|
83
|
+
<%=f.text_field :birth_date_less_than_equal_to %>
|
84
|
+
|
85
|
+
<%=f.label :state%>
|
86
|
+
<%=f.select "address.state_id", [['AL', 1], ...] %> <!-- address is an association of model User -->
|
87
|
+
|
88
|
+
<%=f.label :city %>
|
89
|
+
<%=f.text_field "address.city" %> <!-- address is an association of model User -->
|
90
|
+
|
91
|
+
<%=f.label "name of any one who commented to my posts" %>
|
92
|
+
<%=f.text_field "posts.comments.user.first_name_or_posts.comments.user.last_name" %>
|
93
|
+
<!-- the associations could go even deeper, isn't it POWERFUL? -->
|
94
|
+
|
95
|
+
<%=f.submit %>
|
96
|
+
<% end %>
|
97
|
+
|
98
|
+
<% @users.each do |user| %>
|
99
|
+
<%= # show the attributes of user %>
|
100
|
+
<% end %>
|
101
|
+
```
|
102
|
+
|
103
|
+
5. Add route for the post to url "/xxxxxx" (config/route.rb)
|
104
|
+
```
|
105
|
+
post "/xxxxxx" => "yyyyyyy#zzzzzz"
|
106
|
+
```
|
107
|
+
|
108
|
+
## Demo projects
|
109
|
+
There are 2 demo projects for this gem, one for [Rails 5](https://github.com/yzhanginwa/demo_app_for_rails_simple_search)
|
110
|
+
and one for [Rails 7](https://github.com/yzhanginwa/rails_simple_search_demo). You are encouraged to clone them to your local and
|
111
|
+
get a feel of the power of rails-simple-search.
|
112
|
+
|
113
|
+
## Version history
|
114
|
+
For rails 2.x.x applications, you might want to use the version 0.9.0.
|
115
|
+
|
116
|
+
From version 0.9.1 to 0.9.7, Rails 3 is supported.
|
117
|
+
|
118
|
+
From version 0.9.8 on, this gem started to support Rails 4. Version 0.9.8 is tested under Rails 4.1.1, and version 0.9.9 fixed an issue under
|
119
|
+
Rails 4.2.
|
120
|
+
|
121
|
+
From version 1.1.0 on, we started to support the "or" relation, e.g., we can use field like "first_name_or_last_name".
|
122
|
+
|
123
|
+
From version 1.1.3 on, we started to support Rails 5.
|
124
|
+
|
125
|
+
For Rails 7, please use version 1.2.0.
|
126
|
+
|
127
|
+
## License
|
128
|
+
|
129
|
+
Copyright © 2012 [Ethan Zhang], released under the MIT license
|
data/lib/rails-simple-search.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require_relative 'sql_handler'
|
2
2
|
|
3
3
|
module RailsSimpleSearch
|
4
|
-
DEFAULT_CONFIG = {
|
5
|
-
|
6
|
-
|
4
|
+
DEFAULT_CONFIG = {
|
5
|
+
exact_match: [],
|
6
|
+
or_separator: '_or_'
|
7
|
+
}
|
7
8
|
|
8
9
|
module FixModelName
|
9
10
|
def model_name
|
@@ -16,16 +17,16 @@ module RailsSimpleSearch
|
|
16
17
|
|
17
18
|
def self.inherited(subclass)
|
18
19
|
class << subclass
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
# in rails 3, the call to "form_for" invokes the model_name
|
21
|
+
def model_name
|
22
|
+
ActiveModel::Name.new(self)
|
23
|
+
end
|
23
24
|
end
|
24
|
-
|
25
|
+
|
25
26
|
# to fix an issues in rails 4.2
|
26
27
|
subclass.send(:include, RailsSimpleSearch::FixModelName)
|
27
28
|
end
|
28
|
-
|
29
|
+
|
29
30
|
def self.pre_process(model_name, &procedure)
|
30
31
|
model_name.each { |internal_model_name| pre_process(internal_model_name, &procedure) } if model_name.is_a?(Array)
|
31
32
|
@pre_processors ||= {}
|
@@ -37,7 +38,7 @@ module RailsSimpleSearch
|
|
37
38
|
@pre_processors[model_name]
|
38
39
|
end
|
39
40
|
|
40
|
-
def initialize(model_class, criteria={}, config={})
|
41
|
+
def initialize(model_class, criteria = {}, config = {})
|
41
42
|
@criteria = sanitize_criteria(criteria)
|
42
43
|
@config = DEFAULT_CONFIG.merge(config)
|
43
44
|
@config[:exact_match] = [@config[:exact_match]] unless @config[:exact_match].respond_to?(:map!)
|
@@ -49,21 +50,19 @@ module RailsSimpleSearch
|
|
49
50
|
end
|
50
51
|
|
51
52
|
def load_database_handler(model_class)
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
raise("RailsSimpleSearch only supports ActiveRecord for now")
|
56
|
-
end
|
53
|
+
raise("RailsSimpleSearch only supports ActiveRecord for now") unless model_class.ancestors.include?(ActiveRecord::Base)
|
54
|
+
|
55
|
+
RailsSimpleSearch::Base.send(:include, RailsSimpleSearch::SqlHandler)
|
57
56
|
end
|
58
|
-
|
57
|
+
|
59
58
|
def count
|
60
59
|
@count || 0
|
61
60
|
end
|
62
61
|
|
63
|
-
def add_conditions(
|
64
|
-
@criteria.merge!(
|
62
|
+
def add_conditions(hash = {})
|
63
|
+
@criteria.merge!(hash.stringify_keys)
|
65
64
|
end
|
66
|
-
|
65
|
+
|
67
66
|
def remove_criteria(key)
|
68
67
|
key.each { |internal_key| remove_criteria(internal_key) } if key.is_a?(Array)
|
69
68
|
@criteria.delete(key.to_s)
|
@@ -73,43 +72,33 @@ module RailsSimpleSearch
|
|
73
72
|
@criteria[key.to_s] = value
|
74
73
|
end
|
75
74
|
|
76
|
-
private
|
75
|
+
private
|
77
76
|
|
78
77
|
def method_missing(method, *args)
|
79
78
|
method_str = method.to_s
|
80
79
|
if method_str =~ /^([^=]+)=$/
|
81
|
-
@criteria[
|
82
|
-
else
|
80
|
+
@criteria[::Regexp.last_match(1).to_s] = args[0]
|
81
|
+
else
|
83
82
|
@criteria[method_str]
|
84
83
|
end
|
85
84
|
end
|
86
85
|
|
87
86
|
def parse_field_name(name)
|
88
|
-
result = {}
|
89
87
|
if name =~ /^(.*)?((_(greater|less)_than)(_equal_to)?)$/
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
else
|
94
|
-
result[:operator] = "<"
|
95
|
-
end
|
96
|
-
if $5
|
97
|
-
result[:operator] << "="
|
98
|
-
end
|
88
|
+
field_name = ::Regexp.last_match(1)
|
89
|
+
operator = (::Regexp.last_match(4) == 'greater' ? '>' : '<')
|
90
|
+
operator << '=' if ::Regexp.last_match(5)
|
99
91
|
else
|
100
|
-
|
92
|
+
field_name = name
|
101
93
|
end
|
102
|
-
|
94
|
+
|
95
|
+
[field_name, operator]
|
103
96
|
end
|
104
|
-
|
97
|
+
|
105
98
|
def sanitize_criteria(criteria)
|
106
|
-
criteria
|
99
|
+
criteria ||= {}
|
107
100
|
c = {}
|
108
|
-
criteria.each
|
109
|
-
unless value.blank?
|
110
|
-
c[key] = value
|
111
|
-
end
|
112
|
-
end
|
101
|
+
criteria.each { |key, value| c[key] = value unless value.blank? }
|
113
102
|
c.stringify_keys
|
114
103
|
end
|
115
104
|
end
|
data/lib/sql_handler.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
module RailsSimpleSearch
|
2
|
-
module SqlHandler
|
3
|
-
|
2
|
+
module SqlHandler
|
4
3
|
def init
|
5
|
-
@
|
4
|
+
@model_table_name = @model_class.table_name
|
6
5
|
@joins = {}
|
7
6
|
end
|
8
7
|
|
@@ -26,40 +25,38 @@ module RailsSimpleSearch
|
|
26
25
|
elsif column.respond_to?(:type)
|
27
26
|
column.type == :string || column.type == :text
|
28
27
|
else
|
29
|
-
raise
|
28
|
+
raise 'encountered new version of Rails'
|
30
29
|
end
|
31
30
|
end
|
32
|
-
|
31
|
+
|
33
32
|
def make_joins
|
34
33
|
@joins_str = ''
|
35
34
|
joins = @joins.values
|
36
|
-
joins.sort! {|a,b| a[0] <=> b[0]}
|
35
|
+
joins.sort! { |a, b| a[0] <=> b[0] }
|
37
36
|
joins.each do |j|
|
38
37
|
table = j[1]
|
39
38
|
constrain = j[2]
|
40
|
-
@joins_str << " inner join
|
39
|
+
@joins_str << format(" inner join #{table} AS A%02d on #{constrain}", j[0])
|
41
40
|
end
|
42
41
|
end
|
43
|
-
|
42
|
+
|
44
43
|
def run_criteria
|
45
44
|
@condition_group = ConditionGroup.new
|
46
45
|
@condition_group.set_relation(:and)
|
47
46
|
|
48
47
|
@criteria.each do |key, value|
|
49
|
-
@condition_group.add(
|
48
|
+
@condition_group.add(parse_search_parameters(key, value))
|
50
49
|
end
|
51
50
|
|
52
51
|
make_joins
|
53
52
|
end
|
54
53
|
|
55
|
-
def
|
56
|
-
|
57
|
-
field = name_hash[:field_name]
|
58
|
-
operator = name_hash[:operator]
|
59
|
-
|
54
|
+
def build_single_condition(base_class, association_alias, field, value)
|
55
|
+
field, operator = parse_field_name(field)
|
60
56
|
table = base_class.table_name
|
61
57
|
key = "#{table}.#{field}"
|
62
|
-
|
58
|
+
final_key = "#{association_alias}.#{field}"
|
59
|
+
|
63
60
|
column = base_class.columns_hash[field.to_s]
|
64
61
|
return nil unless column
|
65
62
|
|
@@ -69,68 +66,78 @@ module RailsSimpleSearch
|
|
69
66
|
verb = 'in'
|
70
67
|
elsif operator
|
71
68
|
verb = operator
|
72
|
-
elsif text_column?(column) && ! @config[:exact_match].include?((@
|
69
|
+
elsif text_column?(column) && ! @config[:exact_match].include?((@model_table_name == table) ? field : key)
|
73
70
|
verb = 'like'
|
74
71
|
value = "%#{value}%"
|
75
72
|
else
|
76
73
|
verb = '='
|
77
74
|
end
|
78
75
|
|
79
|
-
ConditionGroup.new(ConditionItem.new(
|
76
|
+
ConditionGroup.new(ConditionItem.new(final_key, verb, value))
|
77
|
+
end
|
78
|
+
|
79
|
+
def table_name_to_alias(table_name)
|
80
|
+
format('A%02d', @joins[table_name][0])
|
80
81
|
end
|
81
|
-
|
82
|
+
|
82
83
|
def insert_join(base_class, asso_ref)
|
83
84
|
base_table = base_class.table_name
|
84
85
|
asso_table = asso_ref.klass.table_name
|
85
|
-
|
86
|
+
|
86
87
|
@join_count ||= 0
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
88
|
+
return if base_table == asso_table
|
89
|
+
return unless @joins[asso_table].nil?
|
90
|
+
|
91
|
+
@join_count += 1
|
92
|
+
base_table_alias = @join_count < 2 ? base_table : table_name_to_alias(base_table)
|
93
|
+
asso_table_alias = format('A%02d', @join_count)
|
94
|
+
|
95
|
+
if asso_ref.belongs_to?
|
96
|
+
@joins[asso_table] =[@join_count, asso_table, "#{base_table_alias}.#{asso_ref.foreign_key} = #{asso_table_alias}.#{asso_ref.klass.primary_key}"]
|
97
|
+
else
|
98
|
+
join_cond = "#{base_table_alias}.#{base_class.primary_key} = #{asso_table_alias}.#{asso_ref.foreign_key}"
|
99
|
+
join_cond = "#{asso_table_alias}.#{asso_ref.type} = '#{base_class.name}' and #{join_cond}" if asso_ref.type
|
100
|
+
@joins[asso_table] = [@join_count, asso_table, join_cond]
|
98
101
|
end
|
99
102
|
end
|
100
|
-
|
101
|
-
|
103
|
+
|
104
|
+
# This method parse a search parameter and its value
|
105
|
+
# then produce a ConditionGroup
|
106
|
+
def parse_search_parameters(attribute, value)
|
107
|
+
# handle _or_ parameters
|
102
108
|
attributes = attribute.split(@config[:or_separator])
|
103
|
-
if
|
109
|
+
if attributes.size > 1
|
104
110
|
cg = ConditionGroup.new
|
105
111
|
cg.set_relation(:or)
|
106
112
|
attributes.each do |a|
|
107
|
-
cg.add(
|
113
|
+
cg.add(parse_search_parameters(a, value))
|
108
114
|
end
|
109
115
|
return cg
|
110
116
|
end
|
111
117
|
|
118
|
+
# handle direct fields
|
112
119
|
unless attribute =~ /\./
|
113
|
-
|
114
|
-
condition = insert_condition(@model_class, attribute, field, value)
|
120
|
+
condition = build_single_condition(@model_class, @model_class.table_name, attribute, value)
|
115
121
|
return condition
|
116
|
-
end
|
122
|
+
end
|
117
123
|
|
124
|
+
# handle association fields
|
118
125
|
association_fields = attribute.split(/\./)
|
119
126
|
field = association_fields.pop
|
120
127
|
|
121
128
|
base_class = @model_class
|
122
|
-
|
129
|
+
until association_fields.empty?
|
123
130
|
association_fields[0] = base_class.reflect_on_association(association_fields[0].to_sym)
|
124
131
|
insert_join(base_class, association_fields[0])
|
125
132
|
base_class = association_fields.shift.klass
|
126
133
|
end
|
127
134
|
|
128
|
-
|
129
|
-
|
135
|
+
association_alias = table_name_to_alias(base_class.table_name)
|
136
|
+
build_single_condition(base_class, association_alias, field, value)
|
130
137
|
end
|
131
|
-
|
132
138
|
end
|
133
139
|
|
140
|
+
# This class holds a single condition
|
134
141
|
class ConditionItem
|
135
142
|
attr_reader :field, :verb, :value
|
136
143
|
|
@@ -141,8 +148,11 @@ module RailsSimpleSearch
|
|
141
148
|
end
|
142
149
|
end
|
143
150
|
|
151
|
+
# This class holds a ConditionGroup
|
152
|
+
# One ConditionGroup can hold one or more
|
153
|
+
# conditions
|
144
154
|
class ConditionGroup
|
145
|
-
def initialize(item=nil)
|
155
|
+
def initialize(item = nil)
|
146
156
|
if item
|
147
157
|
@condition_item = item
|
148
158
|
else
|
@@ -150,17 +160,15 @@ module RailsSimpleSearch
|
|
150
160
|
end
|
151
161
|
end
|
152
162
|
|
153
|
-
def add(
|
154
|
-
if leaf?
|
155
|
-
|
156
|
-
|
157
|
-
@children << cg if cg
|
163
|
+
def add(condition_group)
|
164
|
+
raise "It's not allowed to add child into leaf node" if leaf?
|
165
|
+
|
166
|
+
@children << condition_group if condition_group
|
158
167
|
end
|
159
168
|
|
160
169
|
def set_relation(and_or)
|
161
|
-
if leaf?
|
162
|
-
|
163
|
-
end
|
170
|
+
raise "It's not needed to set relation for leaf node" if leaf?
|
171
|
+
|
164
172
|
@relation = and_or
|
165
173
|
end
|
166
174
|
|
@@ -169,7 +177,7 @@ module RailsSimpleSearch
|
|
169
177
|
end
|
170
178
|
|
171
179
|
def empty?
|
172
|
-
|
180
|
+
@children && @children.empty? ? true : false
|
173
181
|
end
|
174
182
|
|
175
183
|
def to_ar_condition
|
@@ -177,17 +185,14 @@ module RailsSimpleSearch
|
|
177
185
|
if leaf?
|
178
186
|
i = @condition_item
|
179
187
|
condition << "#{i.field} #{i.verb}"
|
180
|
-
|
181
|
-
condition[0] << " (?)"
|
182
|
-
else
|
183
|
-
condition[0] << " ?"
|
184
|
-
end
|
188
|
+
condition[0] << (i.verb == 'in' ? ' (?)' : ' ?')
|
185
189
|
condition << i.value
|
186
190
|
else
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
+
tmp_conditions = @children.map(&:to_ar_condition)
|
192
|
+
tmp_condition_str = tmp_conditions.map(&:first).join(" #{@relation} ")
|
193
|
+
condition << "(#{tmp_condition_str})"
|
194
|
+
tmp_conditions.each do |t|
|
195
|
+
(1..(t.length - 1)).each do |index|
|
191
196
|
condition << t[index]
|
192
197
|
end
|
193
198
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails-simple-search
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yi Zhang
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-12-
|
11
|
+
date: 2023-12-27 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: rails-simple-search is a light and easy to use gem. It could help developers
|
14
14
|
quickly build a search page.
|
@@ -17,7 +17,7 @@ executables: []
|
|
17
17
|
extensions: []
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
|
-
- README.
|
20
|
+
- README.md
|
21
21
|
- lib/rails-simple-search.rb
|
22
22
|
- lib/sql_handler.rb
|
23
23
|
homepage: http://github.com/yzhanginwa/rails-simple-search
|
data/README.rdoc
DELETED
@@ -1,95 +0,0 @@
|
|
1
|
-
=rails-simple-search
|
2
|
-
|
3
|
-
rails-simple-search is a Ruby gem. It helps you quickly implement searching/filtering function for your web site. This plugin has paginating feature built in. If you're not looking for a full-text searching solution, this plugin will most probably satisfy all your searching requirement.
|
4
|
-
|
5
|
-
From time to time, I need to build pages to show a list of narrowed down records from a database table by giving some searching criteria on some columns of the table and/or of some referencing tables. Before I implemented this plugin, I usually do the searching in the following way:
|
6
|
-
|
7
|
-
1. Use <%= form_tag %> to build a form in the view
|
8
|
-
2. Get the searching criteria from the params hash individually in the controller and put them into instance variable to be used in view
|
9
|
-
3. Build the SQL WHERE clause and sometimes the JOIN clause according to the values from the form
|
10
|
-
4. Run the find(:all, :conditions => [xxxxxx], :joins => "yyyyyy") with the WHERE and JOIN clauses
|
11
|
-
|
12
|
-
After having used this pattern a few times, I realized I could DRY it to make future coding of this kind of searching much simpler. That's where the rails-simple-search plugin comes in.
|
13
|
-
|
14
|
-
Now implementing the searching/filter page is a lot easier for me. You're see how easy it is by taking a look at the following example. I may give more examples in the future when I have some spare time.
|
15
|
-
|
16
|
-
|
17
|
-
== Example
|
18
|
-
|
19
|
-
Let's suppose we have models of User, Address, Post and Comment. User model has_one address and has_many posts; Post model has_many comments. We'd like to search for users according to any combination of the following criteria:
|
20
|
-
|
21
|
-
* part of the user's email addrsss
|
22
|
-
* range of the user's birth date
|
23
|
-
* state of the user's address
|
24
|
-
* part of the name of any authors who commented the user's any posts
|
25
|
-
|
26
|
-
The following is how we implement this searching function with rails-simple-search:
|
27
|
-
|
28
|
-
1. Include gem into Gemfile
|
29
|
-
|
30
|
-
gem 'rails-simple-search'
|
31
|
-
|
32
|
-
2. Code in model (app/model/search.rb):
|
33
|
-
|
34
|
-
class Search < RailsSimpleSearch::Base
|
35
|
-
end
|
36
|
-
|
37
|
-
3. Code in controller:
|
38
|
-
|
39
|
-
@search = Search.new(User, params[:search])
|
40
|
-
@users = @search.run.paginate(page: params[:page], per_page: 20).order('email')
|
41
|
-
|
42
|
-
4. Code in views:
|
43
|
-
|
44
|
-
<% form_for @search, url => "/xxxxxx", data: {turbo: false} do |f| %>
|
45
|
-
|
46
|
-
<%=f.label :email %>
|
47
|
-
<%=f.text_field :email %>
|
48
|
-
|
49
|
-
<%=f.label "first name or last name" %>
|
50
|
-
<%=f.text_field "first_name_or_last_name" %>
|
51
|
-
|
52
|
-
<%=f.label :from_birth_date %>
|
53
|
-
<%=f.text_field :birth_date_greater_than_equal_to %>
|
54
|
-
|
55
|
-
<%=f.label :to_age %>
|
56
|
-
<%=f.text_field :birth_date_less_than_equal_to %>
|
57
|
-
|
58
|
-
<%=f.label :state%>
|
59
|
-
<%=f.select "address.state_id", [['AL', 1], ...] %> <!-- address is an association of model User -->
|
60
|
-
|
61
|
-
<%=f.label :post%>
|
62
|
-
<%=f.text_field "posts.comments.author" %> <!-- the associations could go even deeper, isn't it POWERFUL? -->
|
63
|
-
|
64
|
-
<%=f.submit %>
|
65
|
-
<% end %>
|
66
|
-
|
67
|
-
<% @users.each do |user| %>
|
68
|
-
<%= # show the attributes of user %>
|
69
|
-
<% end %>
|
70
|
-
|
71
|
-
5. Add route for the post to url "/xxxxxx" (config/route.rb)
|
72
|
-
post "/xxxxxx" => "yyyyyyy#zzzzzz"
|
73
|
-
|
74
|
-
== Note
|
75
|
-
|
76
|
-
For rails 2.x.x applications, you might want to use the version 0.9.0.
|
77
|
-
|
78
|
-
From version 0.9.1 to 0.9.7, Rails 3 is supported.
|
79
|
-
|
80
|
-
From version 0.9.8 on, this gem started to support Rails 4. Version 0.9.8 is tested under Rails 4.1.1, and version 0.9.9 fixed an issue under
|
81
|
-
Rails 4.2.
|
82
|
-
|
83
|
-
From version 1.1.0 on, we started to support the "or" relation, e.g., we can use field like "first_name_or_last_name".
|
84
|
-
|
85
|
-
From version 1.1.3 on, we started to support Rails 5.
|
86
|
-
|
87
|
-
For Rails 7, please use version 1.1.7.
|
88
|
-
|
89
|
-
There is a real demo application which you can download and run immediately: https://github.com/yzhanginwa/demo_app_for_rails_simple_search
|
90
|
-
|
91
|
-
For Rails 7 demo, please see here: https://github.com/yzhanginwa/rails_simple_search_demo
|
92
|
-
|
93
|
-
== License
|
94
|
-
|
95
|
-
Copyright (c) 2012 [Yi Zhang], released under the MIT license
|