mincer 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 48b963be79455d1a78fb2ba3ba717e7797a1c68b
4
- data.tar.gz: 38811ae437f8dd4e35d338e7abcd1a50988df393
3
+ metadata.gz: e760eb485129e914fb5ac87ab37ded1217ba6114
4
+ data.tar.gz: 99cba1c3544b4b7f294ceeb9f0fe25535e9943a1
5
5
  SHA512:
6
- metadata.gz: 9eaae3dda688bbb12f58fd56ce8850e5570354e8ed232bcff89614617c97dd73cd46018322559ddc5288c51a5e52425d7b5f3492deb89ac8898249a7b1fb995b
7
- data.tar.gz: fd7512ad6cc69e82410e525a517db83657050f6f40bdb0bc0741e97b507239e68ce2918622ded82012f7a74a0c5503f51c5c7177ee67f3fc4fa7bac0eceb1002
6
+ metadata.gz: 09778cda72e7c9fb49542418647e8610c787a0ec5c79176773accc5a89d92593347c6347a76fe518aff83c1f432b78a29ef3b2ee5d14e464476fe61c0dfd0851
7
+ data.tar.gz: b77528eea0f446b5f832a44696c32f74777c375512210ad6412e9173017a746672ec881a905ca68b196c0d78e8cc9f92f510dc91ce2eb6b9a5add5cfe789493a
data/.gitignore CHANGED
@@ -20,3 +20,5 @@ Guardfile
20
20
  atlassian-ide-plugin.xml
21
21
  .rspec
22
22
  spec/database.yml
23
+ .ruby-gemset
24
+ .ruby-version
data/README.md CHANGED
@@ -3,7 +3,14 @@
3
3
  [![Code Climate](https://codeclimate.com/repos/52b775836956801ba7000bdb/badges/ec5d5862e4b89d10695c/gpa.png)](https://codeclimate.com/repos/52b775836956801ba7000bdb/feed)
4
4
  [![Gem Version](https://badge.fury.io/rb/mincer.png)](http://badge.fury.io/rb/mincer)
5
5
 
6
- TODO: Write a gem description
6
+ Mincer is an ActiveRecord::Relation wrapper that applies usefull features to your queries. It can:
7
+
8
+ [Paginate](#pagination)
9
+ [Sort](#sort)
10
+ [Search](#search)
11
+ [Dump to Json(Using postgres >= 9.2)](#json)
12
+ [Generate digest(usefull for caching)](#digest)
13
+
7
14
 
8
15
  ## Installation
9
16
 
@@ -20,8 +27,232 @@ Or install it yourself as:
20
27
  $ gem install mincer
21
28
 
22
29
  ## Usage
30
+ Lets assume we have 2 models
31
+
32
+ class Employee < ActiveRecord::Base
33
+ belongs_to :company
34
+ end
35
+
36
+ class Company < ActiveRecord::Base
37
+ has_many :employees
38
+ end
39
+
40
+ Lets create class EmployeesListQuery class that will inherit from Mincer::Base, and instantiate it
41
+
42
+ class EmployeesListQuery < Mincer::Base
43
+ # method should always return relation
44
+ def build_query(relation, args)
45
+ custom_select = <<-SQL
46
+ employees.id,
47
+ employees.full_name as employee_name,
48
+ companies.name as company_name
49
+ SQL
50
+ relation.joins(:company).select(custom_select)
51
+ end
52
+ end
53
+
54
+ employees = EmployeesListQuery.new(Employee)
55
+
56
+ `employees` will delegate all methods, that it can't find on itself, to relation objects. This means you can use
57
+ `employess` as you would use any ActiveRecord::Relation object:
58
+
59
+ <% employees.each do |employee| %>
60
+ <%= employee.employee_name %>
61
+ <%= employee.company_name %>
62
+ <% end %>
63
+
64
+
65
+ Now lets's look what more can we do with this object
66
+
67
+ <a name="pagination"/>
68
+ ### Pagination
69
+ Mincer supports [kaminari](https://github.com/amatsuda/kaminari) and [will_paginate](https://github.com/mislav/will_paginate). In order to use pagination you need to include one of them
70
+ to your `Gemfile`. Example of using pagination
71
+
72
+ employees = EmployeesListQuery.new(Employee, {'page' => 2, 'per_page' => 10})
73
+
74
+ By default all `Micner` objects will use pagination, even if no arguments are passed. To set default values for pagination please refer to `kaminari` or `will_paginate` documentation.
75
+
76
+ To disable pagination you can use class method `skip_pagination!`:
77
+
78
+ class EmployeesListQuery < Mincer::Base
79
+ skip_pagination!
80
+
81
+ # method should always return relation
82
+ def build_query(relation, args)
83
+ custom_select = <<-SQL
84
+ employees.id,
85
+ employees.full_name as employee_name,
86
+ companies.name as company_name
87
+ SQL
88
+ relation.joins(:company).select(custom_select)
89
+ end
90
+ end
91
+
92
+ <a name="sort"/>
93
+ ### Sorting
94
+
95
+ Example of using sorting:
96
+
97
+ employees = EmployeesListQuery.new(Employee, {'sort' => 'employee_name', 'order' => 'DESC'})
98
+
99
+ By default all Mincer objects will sort by attribute `id` and order `ASC`. To change defaults you can override
100
+ them like this
101
+
102
+ class EmployeesListQuery < Mincer::Base
103
+ # method should always return relation
104
+ def build_query(relation, args)
105
+ custom_select = <<-SQL
106
+ employees.id,
107
+ employees.full_name as employee_name,
108
+ companies.name as company_name
109
+ SQL
110
+ relation.joins(:company).select(custom_select)
111
+ end
112
+
113
+ def default_sort_attribute
114
+ 'employee_name'
115
+ end
116
+
117
+ def default_sort_order
118
+ 'DESC'
119
+ end
120
+ end
121
+
122
+ To disable sorting use class method `skip_sorting!` like this:
123
+
124
+ class EmployeesListQuery < Mincer::Base
125
+ skip_sorting!
126
+
127
+ # method should always return relation
128
+ def build_query(relation, args)
129
+ custom_select = <<-SQL
130
+ employees.id,
131
+ employees.full_name as employee_name,
132
+ companies.name as company_name
133
+ SQL
134
+ relation.joins(:company).select(custom_select)
135
+ end
136
+ end
137
+
138
+ Mincer will validate `sort` and `order` params and will not allow to sort by attributes that do not exist.
139
+ Default white list consists of all atttributes from original scope, in our example `Employee.attribute_name`.
140
+ You can expand the list overriding `allowed_sort_attributes` list like this:
141
+
142
+ def allowed_sort_attributes
143
+ super + %w{employee_name company_name}
144
+ end
145
+ This will allow to sort by all Employee attributes + `employee_name` and `company_name`
146
+
147
+ Or restrict like this:
148
+
149
+ def allowed_sort_attributes
150
+ %w{employee_name}
151
+ end
152
+ in this example sorting allowed only by `employee_name`.
153
+
154
+ #### ActionView
155
+
156
+ If you are using Rails or bare ActionView there are few helper methods at your disposal to help generating links
157
+ for sorting. Currently there are 2 methods available: `sort_url_for` and `sort_class_for`.
158
+
159
+ Example of usage in HAML:
160
+
161
+ %ul
162
+ %li{ :class => (sort_class_for employees, 'id') }
163
+ = link_to 'ID', sort_url_for(employees, 'id')
164
+ %li{ :class => (sort_class_for employees, 'employee_name') }
165
+ = link_to 'Employee', sort_url_for(employees, 'employee_name')
166
+ %li{ :class => (sort_class_for employees, 'company_name') }
167
+ = link_to 'Company', sort_url_for(employees, 'company_name')
168
+
169
+ In this example `li` will receive `class="sorted order_down"` or `class="sorted order_up"` if this attribue was used for search.
170
+ Generated url will be enchanced with `sort` and `order` attributes.
171
+
172
+ <a name="search"/>
173
+ ### Search
174
+
175
+ Currently Mincer uses [Textacular](https://github.com/textacular/textacular) for search. This sets alot of restrictions:
176
+ 1. Works only with postgres
177
+ 2. You have to include `textacular` to your Gemfile
178
+ 3. You have to install postgres extension that [Textacular](https://github.com/textacular/textacular) uses for searching.
179
+
180
+ Example of usage:
181
+
182
+ employees = EmployeesListQuery.new(Employee, {'pattern' => 'whatever'})
183
+
184
+ This will use `simple_search`, and if it will return no entries Mincer will run `fuzzy_search`. For more details on what
185
+ is the difference between them, plese look refer to `textacular` github [page](https://github.com/textacular/textacular).
186
+
187
+ <a name="json"/>
188
+ ### JSON generation
189
+
190
+ Mincer allowes you to dump query result to JSON using [Postgres JSON Functions](http://www.postgresql.org/docs/9.3/static/functions-json.html)
191
+ Didn't had time to do benchmarks - but its' extremely fast.
192
+
193
+ Pros:
194
+
195
+ 1. Speed
196
+ 2. No extra dependencies(you don't need any other JSON generators)
197
+
198
+ Cons:
199
+
200
+ 1. Works only with postgres version >= 9.2
201
+ 2. If you are using ruby methods to generate some fields - you won't be able to use them in Mincer objects(Carrierwave image_urls, resource urls). You will have to duplicate logic inside postgres select query.
202
+
203
+ To dump query result to json string you have to call `to_json` on Mincer object:
204
+
205
+ EmployeesListQuery.new(Employee).to_json
206
+
207
+ In our example it will return something like this
208
+
209
+ "[{\"id\":1,\"employee_name\":\"John Smith\",\"company_name\":\"Microsoft\"},{\"id\":2,\"employee_name\":\"Jane Smith\",\"company_name\":\"37 Signals\"}]"
210
+
211
+ In addition you can pass option `root` to `to_json` method if you need to include root to json string:
212
+
213
+ EmployeesListQuery.new(Employee).to_json(root: 'employees')
214
+ # returns
215
+ "{\"employees\":[{\"id\":1,\"employee_name\":\"John Smith\",\"company_name\":\"Microsoft\"},{\"id\":2,\"employee_name\":\"Jane Smith\",\"company_name\":\"37 Signals\"}]}"
216
+
217
+ <a name="digest"/>
218
+ ### Digest
219
+
220
+ Digest is very usefull for cache invalidation on your views when you are using custom queries. We will modify a bit example:
221
+
222
+ class EmployeesListQuery < Mincer::Base
223
+ digest! %w{employee_updated_at company_updated_at}
224
+
225
+ def build_query(relation, args)
226
+ custom_select = <<-SQL
227
+ employees.id,
228
+ employees.full_name as employee_name,
229
+ companies.name as company_name,
230
+ employees.updated_at as employee_updated_at,
231
+ companies.updated_at as company_updated_at
232
+ SQL
233
+ relation.joins(:company).select(custom_select)
234
+ end
235
+ end
236
+
237
+ In this example we will use 2 updated_at timestamps to generate digest. Whenever one of them will change - digest will change also. To get digest you should use method `digest` on Mincer model
238
+
239
+ EmployeesListQuery.new(Employee).digest # "\\x20e93b4dc5e029130f3d60d697137934"
240
+
241
+ To generate digest you need to install extension 'pgcrypto'. If you use Rails, please use migration for that
242
+
243
+ enable_extension 'pgcrypto'
244
+
245
+ or run `CREATE EXTENSION IF NOT EXISTS pgcrypto;`
246
+
247
+
248
+ ## TODO
249
+
250
+ 1. Create general configuration for Mincer that would allow:
251
+ 1. Changing sort html classes
252
+ 2. Changing default arguments(sort, order, pattern, page, per_page..)
253
+ 3. Disabling some processors for all Mincer objects
254
+ 2. Create rails generators.
23
255
 
24
- TODO: Write usage instructions here
25
256
 
26
257
  ## Contributing
27
258
 
@@ -9,7 +9,7 @@ module Mincer
9
9
  if Mincer.postgres? && !textacular?
10
10
  warn 'You must include "textacular" to your Gemfile to use search'
11
11
  @relation
12
- elsif Mincer.postgres? && @args['pattern']
12
+ elsif Mincer.postgres? && @args['pattern'].present?
13
13
  @relation.basic_search(@args['pattern']).presence || @relation.fuzzy_search(@args['pattern'])
14
14
  else
15
15
  @relation
@@ -7,13 +7,17 @@ module Mincer
7
7
  end
8
8
 
9
9
  def apply
10
- relation = @relation.order("#{sort_attr} #{order_attr}")
10
+ relation = @relation.order(sort_string)
11
11
  @mincer.sort_attribute, @mincer.sort_order = relation.try(:order_values).try(:first).try(:split)
12
12
  relation
13
13
  end
14
14
 
15
+ def sort_string
16
+ sort_attr ? "#{sort_attr} #{order_attr}, #{@mincer.default_sort_attribute}" : "#{@mincer.default_sort_attribute} #{order_attr}"
17
+ end
18
+
15
19
  def sort_attr
16
- (@mincer.allowed_sort_attributes.include?(@args['sort']) && @args['sort']) || @mincer.send(:default_sort_attribute)
20
+ @mincer.allowed_sort_attributes.include?(@args['sort']) && @args['sort']
17
21
  end
18
22
 
19
23
  def order_attr
@@ -1,7 +1,7 @@
1
1
  module Mincer
2
2
 
3
3
  def self.version
4
- Gem::Version.new '0.1.0'
4
+ Gem::Version.new '0.1.1'
5
5
  end
6
6
 
7
7
  module VERSION #:nodoc:
@@ -33,6 +33,11 @@ describe ::Mincer::Processors::Search do
33
33
  query = subject.new(ActiveRecordModel, { 'pattern' => 'Bingo' })
34
34
  query.to_a.count.should eq(1)
35
35
  end
36
+
37
+ it 'avoids search when pattern is an empty string or spaces' do
38
+ query = subject.new(ActiveRecordModel, { 'pattern' => ' ' })
39
+ query.to_a.count.should eq(3)
40
+ end
36
41
  end
37
42
 
38
43
 
@@ -1,4 +1,4 @@
1
1
  def setup_basic_sqlite3_table
2
2
  ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
3
- ActiveRecord::Base.connection.execute('CREATE TABLE active_record_models (id INTEGER UNIQUE, text STRING)')
3
+ ActiveRecord::Base.connection.execute('CREATE TABLE active_record_models (id INTEGER PRIMARY KEY, text STRING)')
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mincer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Krasinsky
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-23 00:00:00.000000000 Z
11
+ date: 2014-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord