lupa 1.0.0 → 1.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 83dad7fde61871e8d0333b609958b6eadaccc23c
4
- data.tar.gz: 1a1eb1e14ede2f72d5f7eb029c38b2511e9182f6
3
+ metadata.gz: 46daf9ac53354ecc21016d968c78de4d322a1bb5
4
+ data.tar.gz: 62c8cde8889723041b1d622ed2bc1bec5f784ff6
5
5
  SHA512:
6
- metadata.gz: e6f6127f879df5ec3bcee9e4c0c479df20aa53c17389fedf7a10d1403900da7920372a6bf2dab44ddad82843465c71834fe3343e515f506d63f331b4feec23b3
7
- data.tar.gz: 84e3f845e779bdb383410f32509b99b4f7ae5fcda658dbb5e3dd49e8110b735c66b47c49ff583a2942822df800fc656fe8315c2f52bdb3b6807e476a5c7b06e0
6
+ metadata.gz: 010ba896da1a30da488d2726646616ca4e1c7091dd5907ae20150756af248b8173266e4b68f501470c35cbf12d933f0f6f530b8943920b2b0f874bff9a782599
7
+ data.tar.gz: 36a3ac1ee305f13318d990f250a40b97ab5f6cb90661c1e546ca4f0b0218e746a05d321d7069944ba100c484e8343125ccb2a8123cc2b15b46e9539e918168e1
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.5
6
+ - 2.2.1
@@ -0,0 +1,4 @@
1
+ ## 1.0.1
2
+
3
+ * enhancements
4
+ * A **Lupa::DefaultSearchAttributesError** exception will be raised if `default_search_attributes` does not return a hash.
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in lupa.gemspec
4
4
  gemspec
5
+
6
+ gem 'coveralls', require: false
data/README.md CHANGED
@@ -2,53 +2,65 @@
2
2
 
3
3
  Lupa means *Magnifier* in spanish.
4
4
 
5
- Lupa lets you create simple, robust and scaleable search filters with ease using regular Ruby classes and object oriented design patterns. It's Framework and ORM agnostic.
5
+ [![Build Status](https://travis-ci.org/edelpero/lupa.svg?branch=master)](https://travis-ci.org/edelpero/lupa) [![Coverage Status](https://coveralls.io/repos/edelpero/lupa/badge.svg?branch=master)](https://coveralls.io/r/edelpero/lupa?branch=master) [![Code Climate](https://codeclimate.com/github/edelpero/lupa/badges/gpa.svg)](https://codeclimate.com/github/edelpero/lupa) [![Inline docs](http://inch-ci.org/github/edelpero/lupa.svg?branch=master)](http://inch-ci.org/github/edelpero/lupa)
6
6
 
7
- ## Search Class
7
+ Lupa lets you create simple, robust and scaleable search filters with ease using regular Ruby classes and object oriented design patterns.
8
8
 
9
- ### Usage
9
+ Lupa it's Framework and ORM agnostic. It will work with any ORM or Object that can build a query using **chained method calls**, like ActiveRecord: `
10
+ Product.where(name: 'Digital').where(category: '23').limit(2)`.
10
11
 
11
- The example will explain how to use the class on a Rails application:
12
+ **Table of Contents:**
12
13
 
13
- - Define a custom form:
14
+ * [Search Class](https://github.com/edelpero/lupa#search-class)
15
+ * [Overview](https://github.com/edelpero/lupa#overview)
16
+ * [Definition](https://github.com/edelpero/lupa#definition)
17
+ * [Public Methods](https://github.com/edelpero/lupa#public-methods)
18
+ * [Default Search Scope](https://github.com/edelpero/lupa#default-search-scope)
19
+ * [Default Search Attributes](https://github.com/edelpero/lupa#default-search-attributes)
20
+ * [Combining Search Classes](https://github.com/edelpero/lupa#combining-search-classes)
21
+ * [Usage with Rails](https://github.com/edelpero/lupa#usage-with-rails)
22
+ * [Testing](https://github.com/edelpero/lupa#testing)
23
+ * [Testing Default Scope](https://github.com/edelpero/lupa#testing-default-scope)
24
+ * [Testing Default Search Attributes](https://github.com/edelpero/lupa#testing-default-search-attributes)
25
+ * [Testing Each Scope Method Individually](https://github.com/edelpero/lupa#testing-each-scope-method-individually)
26
+ * [Installation](https://github.com/edelpero/lupa#installation)
14
27
 
15
- ```haml
16
- # app/views/products/_search.html.haml
17
28
 
18
- = form_tag products_path, method: :get do
19
- = text_field_tag 'name'
20
- = select_tag 'category', options_from_collection_for_select(@categories, 'id', 'name')
21
- = date_field_tag 'created_between[start_date]'
22
- = date_field_tag 'created_between[end_date]'
23
- = submit_tag :search
24
- ```
25
- - Create a new instance of your search class and pass a collection to which all search conditions will be applied and specify the search params you want to apply:
29
+ ## Search Class
30
+
31
+ ### Overview
26
32
 
27
33
  ```ruby
28
- # app/controllers/products_controller.rb
34
+ products = ProductSearch.new(current_user.products).search(name: 'digital', category: '23')
29
35
 
30
- class ProductsController < ApplicationController
31
- def index
32
- @products = ProductSearch.new(current_user.products).search(search_params)
33
- end
34
-
35
- protected
36
- def search_params
37
- params.permit(:name, :category, created_between: [:start_date, :end_date])
38
- end
36
+ # Iterate over the search results
37
+ products.each do |product|
38
+ # Your logic goes here
39
39
  end
40
40
  ```
41
- - Loop through the search results on your view.
41
+ Calling **.each** on the instance will build a search by chaining calls to **name** and **category** methods defined in our **ProductSearch::Scope** class.
42
42
 
43
- ```haml
44
- # app/views/products/index.html.haml
43
+ ```ruby
44
+ # app/searches/product_search.rb
45
45
 
46
- %h1 Products
46
+ class ProductSearch < Lupa::Search
47
+ # Scope class holds all your search methods.
48
+ class Scope
47
49
 
48
- %ul
49
- - @products.each do |product|
50
- %li
51
- = "#{product.name} - #{product.price} - #{product.category}"
50
+ # Search method
51
+ def name
52
+ if search_attributes[:name].present?
53
+ scope.where('name iLIKE ?', "%#{search_attributes[:name]}%")
54
+ end
55
+ end
56
+
57
+ # Search method
58
+ def category
59
+ scope.where(category_id: search_attributes[:category])
60
+ end
61
+
62
+ end
63
+ end
52
64
  ```
53
65
 
54
66
  ### Definition
@@ -65,7 +77,7 @@ end
65
77
  Inside your **Scope** class you must define your scope methods. You'll also be able to access to the following methods inside your scope class: **scope** and **search_attributes**.
66
78
 
67
79
  * **`scope:`** returns the current scope when the scope method is called.
68
- * **`search_attributes:`** returns a hash containing the all search attributes specified.
80
+ * **`search_attributes:`** returns a hash containing the all search attributes specified including the default ones.
69
81
 
70
82
  <u>**Note:**</u> All keys of **`search_attributes`** are symbolized.
71
83
 
@@ -75,32 +87,32 @@ Inside your **Scope** class you must define your scope methods. You'll also be a
75
87
  class ProductSearch < Lupa::Search
76
88
  # Scope class holds all your search methods.
77
89
  class Scope
78
-
90
+
79
91
  # Search method
80
92
  def name
81
93
  if search_attributes[:name].present?
82
94
  scope.where('name LIKE ?', "%#{search_attributes[:name]}%")
83
95
  end
84
96
  end
85
-
97
+
86
98
  # Search method
87
99
  def created_between
88
100
  if created_start_date && created_end_date
89
101
  scope.where(created_at: created_start_date..created_end_date)
90
102
  end
91
103
  end
92
-
104
+
93
105
  # Search method
94
106
  def category
95
- scope.where(category_id: search_attributes[:category])
107
+ scope.where(category_id: search_attributes[:category])
96
108
  end
97
-
109
+
98
110
  private
99
111
  # Parses search_attributes[:created_between][:start_date]
100
112
  def created_start_date
101
113
  search_attributes[:created_between] && search_attributes[:created_between][:start_date].try(:to_date)
102
114
  end
103
-
115
+
104
116
  # Parses search_attributes[:created_between][:end_date]
105
117
  def created_end_date
106
118
  search_attributes[:created_between] && search_attributes[:created_between][:end_date].try(:to_date)
@@ -154,7 +166,7 @@ search.unexisting_method
154
166
  # => Lupa::ResultMethodNotImplementedError: The resulting scope does not respond to unexisting_method method.
155
167
  ```
156
168
 
157
- ## Default Search Scope
169
+ ### Default Search Scope
158
170
 
159
171
  You can define a default search scope if you want to use a search class with an specific resource by overriding the initialize method as follows:
160
172
 
@@ -165,7 +177,7 @@ class ProductSearch < Lupa::Search
165
177
  class Scope
166
178
  ...
167
179
  end
168
-
180
+
169
181
  # Be careful not to change the scope variable name,
170
182
  # otherwise you will experiment some issues.
171
183
  def initialize(scope = Product.all)
@@ -183,7 +195,7 @@ search.first
183
195
  # => #<Product id: 1, name: 'Eames Chair', category_id: 23, created_at: "2015-04-06 18:54:13", updated_at: "2015-04-06 18:54:13" >
184
196
  ```
185
197
 
186
- ## Default Search Attributes
198
+ ### Default Search Attributes
187
199
 
188
200
  Defining default search attributes will cause the scope method to be invoked always.
189
201
 
@@ -194,7 +206,7 @@ class ProductSearch < Lupa::Search
194
206
  class Scope
195
207
  ...
196
208
  end
197
-
209
+
198
210
  # This should always return a hash
199
211
  def default_search_attributes
200
212
  { category: 23 }
@@ -210,7 +222,7 @@ search.search_attributes
210
222
  # => { name: 'chair', category: 42 }
211
223
  ```
212
224
 
213
- ## Combining Search Classes
225
+ ### Combining Search Classes
214
226
 
215
227
  You can reuse your search class in order to keep them DRY.
216
228
 
@@ -221,27 +233,27 @@ A common example is searching records created between two dates. So lets create
221
233
 
222
234
  class CreatedAtSearch < Lupa::Search
223
235
  class Scope
224
-
236
+
225
237
  def created_before
226
238
  ...
227
239
  end
228
-
240
+
229
241
  def created_after
230
242
  ...
231
243
  end
232
-
244
+
233
245
  def created_between
234
246
  if created_start_date && created_end_date
235
247
  scope.where(created_at: created_start_date..created_end_date)
236
248
  end
237
249
  end
238
-
250
+
239
251
  private
240
252
 
241
253
  def created_start_date
242
254
  search_attributes[:created_between] && search_attributes[:created_between][:start_date].try(:to_date)
243
255
  end
244
-
256
+
245
257
  def created_end_date
246
258
  search_attributes[:created_between] && search_attributes[:created_between][:end_date].try(:to_date)
247
259
  end
@@ -256,26 +268,74 @@ Now we can use it in our **ProductSearch** class:
256
268
 
257
269
  class ProductSearch < Lupa::Search
258
270
  class Scope
259
-
271
+
260
272
  def name
261
273
  ...
262
274
  end
263
-
275
+
264
276
  # We use CreatedAtSearch class to perform the search
265
277
  def created_between
266
278
  if search_attributes[:created_between]
267
279
  CreadtedAtSearch.new(scope).search(created_between: search_attributes[:created_between])
268
280
  end
269
281
  end
270
-
282
+
271
283
  def category
272
284
  ...
273
285
  end
274
-
286
+
275
287
  end
276
288
  end
277
289
  ```
278
290
 
291
+ ## Usage with Rails
292
+
293
+ ### Forms
294
+
295
+ Define a custom form:
296
+
297
+ ```haml
298
+ # app/views/products/_search.html.haml
299
+
300
+ = form_tag products_path, method: :get do
301
+ = text_field_tag 'name'
302
+ = select_tag 'category', options_from_collection_for_select(@categories, 'id', 'name')
303
+ = date_field_tag 'created_between[start_date]'
304
+ = date_field_tag 'created_between[end_date]'
305
+ = submit_tag :search
306
+ ```
307
+
308
+ ### Controllers
309
+
310
+ Create a new instance of your search class and pass a collection to which all search conditions will be applied and specify the search params you want to apply:
311
+
312
+ ```ruby
313
+ # app/controllers/products_controller.rb
314
+
315
+ class ProductsController < ApplicationController
316
+ def index
317
+ @products = ProductSearch.new(current_user.products).search(search_params)
318
+ end
319
+
320
+ protected
321
+ def search_params
322
+ params.permit(:name, :category, created_between: [:start_date, :end_date])
323
+ end
324
+ end
325
+ ```
326
+ - Loop through the search results on your view.
327
+
328
+ ```haml
329
+ # app/views/products/index.html.haml
330
+
331
+ %h1 Products
332
+
333
+ %ul
334
+ - @products.each do |product|
335
+ %li
336
+ = "#{product.name} - #{product.price} - #{product.category}"
337
+ ```
338
+
279
339
  ## Testing
280
340
 
281
341
  This is a list of things you should test when creating a search class:
data/Rakefile CHANGED
@@ -8,4 +8,4 @@ Rake::TestTask.new do |t|
8
8
  t.verbose = true
9
9
  end
10
10
 
11
-
11
+ task :default => :test
@@ -2,6 +2,7 @@ require "lupa/version"
2
2
 
3
3
  module Lupa
4
4
  DefaultScopeError = Class.new(StandardError)
5
+ DefaultSearchAttributesError = Class.new(StandardError)
5
6
  ScopeMethodNotImplementedError = Class.new(NotImplementedError)
6
7
  ResultMethodNotImplementedError = Class.new(NotImplementedError)
7
8
  SearchAttributesError = Class.new(StandardError)
@@ -314,9 +314,18 @@ module Lupa
314
314
  #
315
315
  # Sets @search_attributes by merging default search attributes with the ones passed to search method.
316
316
  def set_search_attributes(attributes)
317
+ attributes = merge_search_attributes(attributes)
317
318
  attributes = symbolize_keys(attributes)
318
319
  attributes = remove_blank_attributes(attributes)
319
- @search_attributes = default_search_attributes.merge(attributes)
320
+
321
+ @search_attributes = attributes
322
+ end
323
+
324
+ # Internal: Merge search attributes with default search attributes
325
+ def merge_search_attributes(attributes)
326
+ return default_search_attributes.merge(attributes) if default_search_attributes.kind_of?(Hash)
327
+
328
+ raise Lupa::DefaultSearchAttributesError, "default_search_attributes doesn't return a Hash."
320
329
  end
321
330
 
322
331
  # Internal: Symbolizes all keys passed to the search attributes.
@@ -1,3 +1,3 @@
1
1
  module Lupa
2
- VERSION = "1.0.0"
2
+ VERSION = "1.0.1"
3
3
  end
@@ -32,6 +32,22 @@ class ClassWithoutDefaultSearchAttributesSearch < Lupa::Search
32
32
 
33
33
  end
34
34
 
35
+ class ClassWithInvalidDefaultSearchAttributesSearch < Lupa::Search
36
+
37
+ class Scope
38
+ def reverse; end
39
+ end
40
+
41
+ def initialize(scope = [1, 2, 3, 4])
42
+ @scope = scope
43
+ end
44
+
45
+ def default_search_attributes
46
+ 1
47
+ end
48
+
49
+ end
50
+
35
51
 
36
52
  describe Lupa::Search do
37
53
  before do
@@ -61,6 +77,12 @@ describe Lupa::Search do
61
77
  search.default_search_attributes.must_equal params
62
78
  end
63
79
  end
80
+
81
+ context 'when default_search_attributes does not return a Hash' do
82
+ it 'raises a Lupa::DefaultSearchAttributesError exception' do
83
+ proc { ClassWithInvalidDefaultSearchAttributesSearch.search({}).results }.must_raise Lupa::DefaultSearchAttributesError
84
+ end
85
+ end
64
86
  end
65
87
 
66
88
  describe '#results' do
@@ -1,3 +1,6 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
1
4
  require 'minitest/autorun'
2
5
 
3
6
  def context(*args, &block)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lupa
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ezequiel Delpero
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-08 00:00:00.000000000 Z
11
+ date: 2015-04-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -61,6 +61,8 @@ extensions: []
61
61
  extra_rdoc_files: []
62
62
  files:
63
63
  - .gitignore
64
+ - .travis.yml
65
+ - CHANGELOG.md
64
66
  - Gemfile
65
67
  - LICENSE.txt
66
68
  - README.md