rooble 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +145 -19
  3. data/lib/rooble.rb +76 -42
  4. data/lib/rooble/version.rb +1 -1
  5. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6c2be4d76fd38aaa62e61fb049d0f2f96416f389
4
- data.tar.gz: f5d3645b00bee7dc2f3889fa2d81a835dc01e870
3
+ metadata.gz: 48792701842c670bc199dec1e90fc047aab59f7d
4
+ data.tar.gz: a88e321916bd3e9c96b6c891ce29058555b52206
5
5
  SHA512:
6
- metadata.gz: 20661aae7465f984802c1db7e7d3b6f411164b7f418f7330da53046121969d597e3ab5d80e24885e4443968f69d7eb5015e5e6dc7fd2f625f4a33d748bdee406
7
- data.tar.gz: 944cd7cbef4b35670202245083c6e21c4de9c343bab3c470b257e74b73b63b628ed73fcac1e8df9a93658735bf263d9b33bc47878ea46b621474af8d20b2703f
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
- ## Why?
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
- ## How?
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
- Add this to your Gemfile:
11
+ ## Installation and configuration
12
12
 
13
- `gem 'rooble'`
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
- ## Using it
29
+ ``` ruby
30
+ Rooble.configure do |config|
31
+ config.max_records_per_page = 5
32
+ end
33
+ ```
20
34
 
21
- First create a configuration file by running `rails g rooble:config` which will create `config/initializers/rooble.rb` and setup the default value for `max_records_per_page` to a sane number, you don't want to make it a high number.
35
+ ## Pagination
22
36
 
23
- There are three main methods:
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
- `Model.paginate(1)` will give you records for the first page. You can 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 `Model.paginate(1, 10)` would grab the 10 records for the 1st page.
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
- `Model.search("John", [:name])` will look for records which have **John** within the `name` attribute.
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
- * `type: all` type of match, beginning, end or the whole string. Default is all.
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
- If you want to search for attributes in joint models you would do something like this:
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
- `State.search("USA", "countries.name", join: [:country]).first`
160
+ ``` ruby
161
+ State.search("countries.name", "USA", join: [:country]).first
162
+ ```
41
163
 
42
- Or if you want to search for the first city of the USA:
164
+ Do note the string notation for the relation.
43
165
 
44
- `City.search("USA", "countries.name", join: {state: :country}).first`
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
- That would search the first state that has a country named USA. Do note the string notation for the relation.
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
- `State.search("USA", "countries.name", join: [:country]).paginate(2)`
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(search_term, fields, options={})
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
- # Build the query
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
- records = self.includes(options[:include]).where(query)
68
+ model = self.includes(options[:include])
104
69
  elsif options.has_key? :join
105
- records = self.joins(options[:join]).where(query)
70
+ model = self.joins(options[:join])
106
71
  else
107
- records = self.where(query)
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)
@@ -1,3 +1,3 @@
1
1
  module Rooble
2
- VERSION = "0.1.4"
2
+ VERSION = "0.2.0"
3
3
  end
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.1.4
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-05-22 00:00:00.000000000 Z
11
+ date: 2016-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport