rooble 0.1.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +145 -19
- data/lib/rooble.rb +76 -42
- data/lib/rooble/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48792701842c670bc199dec1e90fc047aab59f7d
|
4
|
+
data.tar.gz: a88e321916bd3e9c96b6c891ce29058555b52206
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d7db4a396a694b043bbdeb60d5aaaa1c0aac32a8577c410ca1df963a84e1e1abee644b265dc36ebd08c79351f114e3d5ccaf02f2993859231ad2dbfe5eb06b4
|
7
|
+
data.tar.gz: 15d4aa0ee364a70140363a277e0e0e354c5fdea107661eac6de01218e461a3558c6f8b80a7583b0d5cdab4885988a608a791ac45e132b35a2aa93c1f8ff3dda3
|
data/README.md
CHANGED
@@ -2,55 +2,181 @@
|
|
2
2
|
|
3
3
|
Yet another unnecessary and selfishly-created pagination and search gem for rails.
|
4
4
|
|
5
|
-
|
5
|
+
**But... Why?**
|
6
6
|
|
7
7
|
Because its a fun way for me to learn rails. There are other, more complex and full featured solutions like [Kaminari](https://github.com/amatsuda/kaminari) but this works fine too.
|
8
8
|
|
9
|
-
|
9
|
+
This gem does not use any special classes or modules, instead it will extend ActiveRecord functionality so that you can use the pagination and search methods directly in your ActiveRecord associations so you can even chain them!
|
10
10
|
|
11
|
-
|
11
|
+
## Installation and configuration
|
12
12
|
|
13
|
-
|
13
|
+
First add this to your Gemfile:
|
14
|
+
|
15
|
+
`gem 'rooble'`
|
14
16
|
|
15
17
|
Then do the usual dance:
|
16
18
|
|
17
|
-
`bundle install`
|
19
|
+
`bundle install`
|
20
|
+
|
21
|
+
Finally create a configuration file by running
|
22
|
+
|
23
|
+
`rails g rooble:config`
|
24
|
+
|
25
|
+
That will create a `config/initializers/rooble.rb` file and setup the default value for `max_records_per_page` to a sane number, you don't want to make it a high number though as it will pull that many records per page.
|
26
|
+
|
27
|
+
Please note that you can create the initializer manually if you'd like to, this is the basic template:
|
18
28
|
|
19
|
-
|
29
|
+
``` ruby
|
30
|
+
Rooble.configure do |config|
|
31
|
+
config.max_records_per_page = 5
|
32
|
+
end
|
33
|
+
```
|
20
34
|
|
21
|
-
|
35
|
+
## Pagination
|
22
36
|
|
23
|
-
There are
|
37
|
+
There are two methods for pagination that you can use directly to your ActiveRecord associations and models:
|
38
|
+
|
39
|
+
``` ruby
|
40
|
+
Model.pages(max_records_per_page=nil)
|
41
|
+
```
|
42
|
+
|
43
|
+
**Arguments:**
|
44
|
+
|
45
|
+
- `max_records_per_page` overrides the default value and sets how many records to divide per page
|
24
46
|
|
25
47
|
`Model.pages` without any parameters will give you the number of available pages considering the `max_records_per_page` default. You can override it and pass the number of max records that should be counted per page so you get the total count of pages for such number doing something like `Model.pages(10)` if you want the count of pages with 10 records per page.
|
26
48
|
|
27
|
-
|
49
|
+
For instance given that we have a `states` table with the 48 _contiguous_ US states and our `max_records_per_page` is set to `10`:
|
50
|
+
|
51
|
+
``` ruby
|
52
|
+
State.pages
|
53
|
+
# => 5
|
54
|
+
```
|
55
|
+
|
56
|
+
We can override the `max_records_per_page` as well on the first argument which is optional so we get a count of pages for a diferent amount of records per page:
|
57
|
+
|
58
|
+
``` ruby
|
59
|
+
State.pages(20)
|
60
|
+
# => 3
|
61
|
+
```
|
62
|
+
|
63
|
+
``` ruby
|
64
|
+
Model.paginate(page=1, max_records_per_page=nil)
|
65
|
+
```
|
66
|
+
|
67
|
+
**Arguments:**
|
68
|
+
|
69
|
+
- `page` the page to pull records from
|
70
|
+
- `max_records_per_page` how many records to generate per page, this overrides the initializer option
|
71
|
+
|
72
|
+
`Model.paginate` will pull records for the first page. The first argument is the page number and you can also override the `max_records_per_page` config option on the second argument so that you paginate a specific page with a different amount of max records, for instance:
|
73
|
+
|
74
|
+
``` ruby
|
75
|
+
State.paginate
|
76
|
+
# => pulls 10 records corresponding to the 1st page
|
77
|
+
```
|
78
|
+
|
79
|
+
``` ruby
|
80
|
+
State.paginate(2)
|
81
|
+
# => pulls 10 records corresponding to the 2nd page
|
82
|
+
```
|
83
|
+
|
84
|
+
``` ruby
|
85
|
+
State.paginate(2, 5)
|
86
|
+
# => pulls only 5 records corresponding to the 2nd page
|
87
|
+
```
|
88
|
+
|
89
|
+
Note that in the 3rd example we override the `max_records_per_page` so we will get more pages since we lowered the number of records.
|
28
90
|
|
29
|
-
|
91
|
+
## Search
|
92
|
+
|
93
|
+
The search method will lookup for keyword matches within columns or records that match a given id, this means that you can essentially use it only to search in text fields or in the primary key. So for instance if you try to search against numeric columns it won't work but for that type of specific search you are better off using ActiveRecord directly. The rational behind this is that most application _open_ searches will lookup for id's, names or codes, rarely against numeric values.
|
94
|
+
|
95
|
+
``` ruby
|
96
|
+
Model.search(fields, search_term, options={})
|
97
|
+
```
|
98
|
+
|
99
|
+
**Arguments:**
|
100
|
+
|
101
|
+
- `fields` a string with a field name or string array with field names to make the query against
|
102
|
+
- `search_term` string with the term to search
|
103
|
+
- `options` a hash with a set of options. Please se below for more info.
|
104
|
+
|
105
|
+
`Model.search("name", "John")` will look for records which have **John** within the `name` attribute.
|
30
106
|
|
31
107
|
There is an extra options hash that you can pass to this method, the options are:
|
32
108
|
|
33
|
-
* `case_sensitive: false` whether you want to make the search case sensitive. Default is false.
|
34
|
-
* `
|
109
|
+
* `case_sensitive: false` whether you want to make the search case sensitive. Default is false. Note that this relies on the database engine defaults so if the column or table schema is set to be case insensitive it won't mater what you set here.
|
110
|
+
* `match_type: all` type of match, `beginning`, `end`, `all` string or `none`. Default is all.
|
35
111
|
* `include` an array/hash of symbols if you want to include other relations on the model. Same as with default rails include.
|
36
112
|
* `join` an array/hash of symbols if you want to join other relations on the model. Same as with default rails join.
|
37
113
|
|
38
|
-
|
114
|
+
**Examples**
|
115
|
+
|
116
|
+
Simple search for a specific state:
|
117
|
+
|
118
|
+
``` ruby
|
119
|
+
State.search("name", "California")
|
120
|
+
# => Yields 1 result
|
121
|
+
```
|
122
|
+
|
123
|
+
Searching for substrings:
|
124
|
+
|
125
|
+
``` ruby
|
126
|
+
State.search("name", "New")
|
127
|
+
# => Yields 4 results: New Mexico, New Jersey, New Hampshire, New York
|
128
|
+
```
|
129
|
+
|
130
|
+
Searching for patterns:
|
131
|
+
|
132
|
+
``` ruby
|
133
|
+
State.search("name", "Ma", match_type: :beginning)
|
134
|
+
# => Yields 3 results where the value starts with Ma: Maine, Maryland and Massachusetts
|
135
|
+
```
|
136
|
+
|
137
|
+
``` ruby
|
138
|
+
State.search("name", "Ma", match_type: :end)
|
139
|
+
# => Yields 2 results where the value ends with ma: Alabama, Oklahoma
|
140
|
+
```
|
141
|
+
|
142
|
+
Searching in several fields
|
143
|
+
|
144
|
+
``` ruby
|
145
|
+
State.search(["id", "name"], "California")
|
146
|
+
# => Yields 1 result which is California
|
147
|
+
```
|
148
|
+
|
149
|
+
The above may not make much sense right? But what happens if you want your users to be able to search from a simple input or search box and allow them to either search states by name or the id? Bingo!:
|
150
|
+
|
151
|
+
``` ruby
|
152
|
+
State.search(["id", "name"], 4)
|
153
|
+
# => Yields 1 result which is California
|
154
|
+
```
|
155
|
+
|
156
|
+
This means that you can allow input for both id's or text and still try to filter by either, you just pass on the second argument whatever input you get from the user.
|
157
|
+
|
158
|
+
Now lets do a more complicated search. Say you want to search for attributes in joint models, like searching for the first state where the country name is "USA":
|
39
159
|
|
40
|
-
|
160
|
+
``` ruby
|
161
|
+
State.search("countries.name", "USA", join: [:country]).first
|
162
|
+
```
|
41
163
|
|
42
|
-
|
164
|
+
Do note the string notation for the relation.
|
43
165
|
|
44
|
-
|
166
|
+
You can even do multi-level joins, for instance if you want to search for the first city of the USA:
|
45
167
|
|
46
|
-
|
168
|
+
``` ruby
|
169
|
+
City.search("countries.name", "USA", join: {state: :country}).first
|
170
|
+
```
|
47
171
|
|
48
172
|
Oh yes, you can chain the methods so you could do something like paginating search results:
|
49
173
|
|
50
|
-
|
174
|
+
``` ruby
|
175
|
+
State.search("countries.name", "USA", join: [:country]).paginate(2)
|
176
|
+
```
|
51
177
|
|
52
178
|
That's it for now.
|
53
179
|
|
54
180
|
## Other features
|
55
181
|
|
56
|
-
I will/may add a view helper to generate the pagination navigation.
|
182
|
+
I will/may add a rails view helper to generate the pagination navigation.
|
data/lib/rooble.rb
CHANGED
@@ -20,39 +20,6 @@ module Rooble
|
|
20
20
|
yield(configuration) if block_given?
|
21
21
|
end
|
22
22
|
|
23
|
-
def self.build_query(search_term, fields, options={})
|
24
|
-
|
25
|
-
# Set whether we want case sensitive search
|
26
|
-
operator = "ILIKE"
|
27
|
-
if options.has_key? :case_sensitive
|
28
|
-
operator = "LIKE" if options[:case_sensitive]
|
29
|
-
end
|
30
|
-
|
31
|
-
search_beginning = "%"
|
32
|
-
search_end = "%"
|
33
|
-
if options.has_key? :type
|
34
|
-
case options[:type]
|
35
|
-
when "beginning"
|
36
|
-
search_end = ""
|
37
|
-
when "end"
|
38
|
-
search_beginning = ""
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
# Loop through fields and build query
|
43
|
-
query = ""
|
44
|
-
or_cond = ""
|
45
|
-
fields = [].push(fields) unless fields.is_a? Array
|
46
|
-
fields.each_with_index do |v,i|
|
47
|
-
if i > 0
|
48
|
-
or_cond = "OR"
|
49
|
-
end
|
50
|
-
query += " #{or_cond} #{v} #{operator} '#{search_beginning}#{search_term}#{search_end}' "
|
51
|
-
end
|
52
|
-
|
53
|
-
query
|
54
|
-
end
|
55
|
-
|
56
23
|
module ClassMethods
|
57
24
|
##
|
58
25
|
# Returns the amount of available pages to do pagination.
|
@@ -85,30 +52,97 @@ module Rooble
|
|
85
52
|
# Searches through records for the given fields
|
86
53
|
#
|
87
54
|
|
88
|
-
def search(
|
55
|
+
def search(fields, search_term, options={})
|
89
56
|
if search_term.nil?
|
90
57
|
raise Rooble::Error.new "You need to give a search term"
|
91
58
|
end
|
92
59
|
|
93
|
-
if fields.empty?
|
60
|
+
if fields.nil? || fields.empty?
|
94
61
|
raise Rooble::Error.new "You need to give at least one field to search"
|
95
62
|
end
|
96
63
|
|
97
64
|
raise Rooble::Error.new("You can only include or join relations, not both!") if ([:include, :join] - options.keys ).empty?
|
98
65
|
|
99
|
-
#
|
100
|
-
query = Rooble::build_query(search_term, fields, options)
|
101
|
-
|
66
|
+
# check if we are joining/including other models first
|
102
67
|
if options.has_key? :include
|
103
|
-
|
68
|
+
model = self.includes(options[:include])
|
104
69
|
elsif options.has_key? :join
|
105
|
-
|
70
|
+
model = self.joins(options[:join])
|
106
71
|
else
|
107
|
-
|
72
|
+
model = self
|
73
|
+
end
|
74
|
+
|
75
|
+
search_beginning = "%"
|
76
|
+
search_end = "%"
|
77
|
+
fields = [].push(fields) unless fields.is_a? Array
|
78
|
+
search_values = []
|
79
|
+
query = ''
|
80
|
+
case_sensitive = false
|
81
|
+
or_cond = ''
|
82
|
+
|
83
|
+
if options.has_key? :case_sensitive
|
84
|
+
if options[:case_sensitive]
|
85
|
+
case_sensitive = true
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
fields.each_with_index do |field,index|
|
90
|
+
# set the OR if we have more than one field
|
91
|
+
if index > 0
|
92
|
+
or_cond = "OR"
|
93
|
+
end
|
94
|
+
|
95
|
+
# lets find out if we are looking for the ID, we can't
|
96
|
+
# use like for integers so we use equality instead
|
97
|
+
if field.downcase == "id"
|
98
|
+
# check that the serch term is actually a number
|
99
|
+
operator = "="
|
100
|
+
search_values.push(search_term.to_i)
|
101
|
+
else
|
102
|
+
|
103
|
+
# set the type of search wether just beginning of string
|
104
|
+
# the end or as a whole
|
105
|
+
if options.has_key? :match_type
|
106
|
+
case options[:match_type].to_s
|
107
|
+
when "beginning"
|
108
|
+
search_beginning = nil
|
109
|
+
when "end"
|
110
|
+
search_end = nil
|
111
|
+
when "all"
|
112
|
+
search_beginning = "%"
|
113
|
+
search_end = "%"
|
114
|
+
when "none"
|
115
|
+
search_beginning = nil
|
116
|
+
search_end = nil
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# set whether we want case sensitive search
|
121
|
+
case ActiveRecord::Base.connection.adapter_name
|
122
|
+
when "PostgreSQL"
|
123
|
+
case_sensitive ? operator = "LIKE" : operator = "ILIKE"
|
124
|
+
search_value = search_term
|
125
|
+
when "MySQL", "Mysql2", "SQLite"
|
126
|
+
operator = "LIKE"
|
127
|
+
if case_sensitive
|
128
|
+
search_value = search_term
|
129
|
+
else
|
130
|
+
field = "LOWER(#{field})"
|
131
|
+
search_value = search_term.downcase
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# downcase the search term if we are doing case insensitive search so
|
136
|
+
# the value of downcasing the column matches
|
137
|
+
search_values.push("#{search_beginning}#{search_value}#{search_end}")
|
138
|
+
end
|
139
|
+
|
140
|
+
query += " #{or_cond} #{field} #{operator} ? "
|
108
141
|
end
|
109
142
|
|
110
|
-
records
|
143
|
+
records = model.where(query, *search_values)
|
111
144
|
end
|
145
|
+
|
112
146
|
end
|
113
147
|
|
114
148
|
ActiveRecord::Base.send(:include, Rooble)
|
data/lib/rooble/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rooble
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gustavo Rubio
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-06-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|