index_query_builder 0.0.1 → 0.0.2
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/.editorconfig +13 -0
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/Gemfile.lock +5 -5
- data/README.md +80 -4
- data/config/{database.yml → database.yml.sample} +0 -0
- data/lib/index_query_builder.rb +20 -22
- data/lib/index_query_builder/query_definition.rb +13 -13
- data/lib/index_query_builder/version.rb +1 -1
- data/spec/index_query_builder_spec.rb +8 -8
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ea38e4a701413196312ab11d0477be169272c3b
|
4
|
+
data.tar.gz: 59431b3ac8e08468b91469a04eb79206355093a7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a107eef8cd790695cf35a119e4e145a28bc312d5f20df41a90b62926231d99c22182487b0b6ba4b4f3c31f39f093e629fb5cc2f97a508d37cd8c3781a7ecff2e
|
7
|
+
data.tar.gz: 55ce20fbdda01589d68161cb43164aeb1ea7c3deb958c910c14d33d80579fb3cb61fff6494358a8b09eb8e650ee98afdb33eff29701257a5c6406de8bebc633d
|
data/.editorconfig
ADDED
data/.gitignore
CHANGED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
index_query_builder (0.0.
|
4
|
+
index_query_builder (0.0.2)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -49,13 +49,13 @@ PLATFORMS
|
|
49
49
|
ruby
|
50
50
|
|
51
51
|
DEPENDENCIES
|
52
|
-
activerecord (~> 4.0.0)
|
53
|
-
activesupport (~> 4.0.0)
|
52
|
+
activerecord (~> 4.0, >= 4.0.0)
|
53
|
+
activesupport (~> 4.0, >= 4.0.0)
|
54
54
|
bundler (~> 1.7)
|
55
55
|
index_query_builder!
|
56
56
|
pg (~> 0.18.1)
|
57
57
|
rake (~> 10.0)
|
58
|
-
rspec (~> 3.3.0)
|
58
|
+
rspec (~> 3.3, >= 3.3.0)
|
59
59
|
|
60
60
|
BUNDLED WITH
|
61
|
-
1.
|
61
|
+
1.12.5
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# IndexQueryBuilder
|
2
2
|
|
3
|
-
|
3
|
+
This gem provides a DSL on top of ActiveRecord to get collection of models for index pages with filters.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -20,17 +20,93 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
-
|
23
|
+
Let's say you have the following schema:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
create_table :posts, force: true do |t|
|
27
|
+
t.text :title
|
28
|
+
t.integer :view_count
|
29
|
+
end
|
30
|
+
|
31
|
+
create_table :comments, force: true do |t|
|
32
|
+
t.integer :post_id
|
33
|
+
t.text :text
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
And the following models
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
class Post < ActiveRecord::Base
|
41
|
+
has_many :comments
|
42
|
+
end
|
43
|
+
|
44
|
+
class Comment < ActiveRecord::Base
|
45
|
+
belongs_to :post
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
And you are building an index page for posts with the following requirements:
|
50
|
+
* Posts should be ordered by `view_count`.
|
51
|
+
* The user should be able to filter posts by texts in post's comments. For example, if `filters` is `{ comment_text: 'This post is amazing' }`, then we should return all the posts with a comment containing `'This post is amazing'`.
|
52
|
+
* More filters will be added soon.
|
53
|
+
|
54
|
+
Without Index Query Builder, you will probably have to do something like this.
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
conditions_strings = []
|
58
|
+
conditions_params = {}
|
59
|
+
|
60
|
+
unless filters[:comment_text].blank?
|
61
|
+
conditions_strings << "comments.text ILIKE :comment_text"
|
62
|
+
conditions_params[:comment_text] = "%#{filters[:comment_text]}%"
|
63
|
+
end
|
64
|
+
|
65
|
+
conditions = (conditions_params.empty? ? "" : [conditions_strings.join(" AND "), conditions_params])
|
66
|
+
|
67
|
+
joins_list = []
|
68
|
+
joins_list << {:posts => :comment} if filters[:comment_text].present?
|
69
|
+
|
70
|
+
posts = Post.where(conditions).joins(joins_list).order("expected_ship_at desc, id desc")
|
71
|
+
```
|
72
|
+
|
73
|
+
Or, with Index Query Builder, you can just write this.
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
posts = IndexQueryBuilder.query Post, with: filters do |query|
|
77
|
+
query.filter_field [:comments, :text], contains: :comment_text
|
78
|
+
query.order_by "view_count DESC"
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
## Operators
|
83
|
+
|
84
|
+
Operators will apply where clauses to query *only if* the filter_name is present in filters hash.
|
85
|
+
|
86
|
+
* :equal_to applies field_name = filter_value
|
87
|
+
* :contains applies substring (ILIKE '%filter_value%')
|
88
|
+
* :greater_than_or_equal_to applies field_name >= filter_value
|
89
|
+
* :less_than applies field_name < filter_value
|
90
|
+
* :present_if applies:
|
91
|
+
* field_name IS NOT NULL if filter_value
|
92
|
+
* field_name IS NULL if !filter_value
|
93
|
+
|
94
|
+
Find more in the [docs](http://www.rubydoc.info/gems/index_query_builder)
|
24
95
|
|
25
96
|
## Running tests
|
26
97
|
|
27
|
-
|
98
|
+
It requires PostgreSQL.
|
99
|
+
|
100
|
+
$ cp config/database.yml.sample config/database.yml
|
101
|
+
|
102
|
+
Update `config/database.yml` with your connection information.
|
103
|
+
|
28
104
|
$ rake db:test:setup
|
29
105
|
$ rspec
|
30
106
|
|
31
107
|
## Contributing
|
32
108
|
|
33
|
-
1. Fork it ( https://github.com/
|
109
|
+
1. Fork it ( https://github.com/arturopie/index_query_builder/fork )
|
34
110
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
35
111
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
36
112
|
4. Push to the branch (`git push origin my-new-feature`)
|
File without changes
|
data/lib/index_query_builder.rb
CHANGED
@@ -6,8 +6,8 @@ require "index_query_builder/version"
|
|
6
6
|
|
7
7
|
# Simple DSL for building queries using filters.
|
8
8
|
#
|
9
|
-
#
|
10
|
-
#
|
9
|
+
# This module makes it easy to fetch records from the database, specially
|
10
|
+
# for showing and filtering records in an index page
|
11
11
|
#
|
12
12
|
module IndexQueryBuilder
|
13
13
|
|
@@ -16,21 +16,20 @@ module IndexQueryBuilder
|
|
16
16
|
# @param base_scope [Arel] used to build query on top of this scope
|
17
17
|
# @param options [Hash]
|
18
18
|
# @option :with [Hash] filters used to build query. Key is filter name, value is value for the filter
|
19
|
-
# @
|
19
|
+
# @yield [QueryDefinition] Gives a QueryDefinition object that implements a DSL to define how to apply filters
|
20
20
|
# @return [Arel] returns arel object to make it easy to extend query (e.g. add pagination, etc)
|
21
21
|
#
|
22
22
|
# ==== Example
|
23
23
|
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
# query.order_by "expected_delivery_at DESC, receive_orders.id DESC"
|
32
|
-
# end
|
24
|
+
# posts = IndexQueryBuilder.query Post.where(:user_id => user.id), with: { title: 'DSLs are awesome' } do |query|
|
25
|
+
# query.filter_field :posted
|
26
|
+
# query.filter_field :title, contains: :title
|
27
|
+
# query.filter_field :posted_date,
|
28
|
+
# greater_than_or_equal_to: :from_posted_date, less_than: :to_posted_date
|
29
|
+
# query.filter_field [:comments, :author, :name], equal_to: :comment_author_name
|
33
30
|
#
|
31
|
+
# query.order_by "posted_date DESC, posts.created_at DESC"
|
32
|
+
# end
|
34
33
|
def self.query(base_scope, options={}, &block)
|
35
34
|
query_definition = QueryDefinition.new
|
36
35
|
block.call(query_definition)
|
@@ -46,21 +45,20 @@ module IndexQueryBuilder
|
|
46
45
|
# @param base_scope [Arel] used to build query on top of this scope
|
47
46
|
# @param options [Hash]
|
48
47
|
# @option options [Hash] :with filters used to build query. Key is filter name, value is value for the filter
|
49
|
-
# @
|
48
|
+
# @yield [QueryDefinition] Gives a QueryDefinition object that implements a DSL to define how to apply filters
|
50
49
|
# @return [Arel] returns arel object to make it easy to extend query (e.g. add pagination, etc)
|
51
50
|
#
|
52
51
|
# ==== Example
|
53
52
|
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
# query.order_by "expected_delivery_at DESC, receive_orders.id DESC"
|
62
|
-
# end
|
53
|
+
# comments = IndexQueryBuilder.query_children :comments, Comments, with: { title: 'DSLs are awesome' } do |query|
|
54
|
+
# query.filter_field :posted
|
55
|
+
# query.filter_field :title, contains: :title
|
56
|
+
# query.filter_field :posted_date,
|
57
|
+
# greater_than_or_equal_to: :from_posted_date, less_than: :to_posted_date
|
58
|
+
# query.filter_field [:comments, :author, :name], equal_to: :comment_author_name
|
63
59
|
#
|
60
|
+
# query.order_by "comment_date DESC"
|
61
|
+
# end
|
64
62
|
def self.query_children(child_association, base_scope, options={}, &block)
|
65
63
|
parents = query(base_scope.eager_load(child_association), options, &block)
|
66
64
|
|
@@ -18,30 +18,30 @@ module IndexQueryBuilder
|
|
18
18
|
#
|
19
19
|
# ==== Operators
|
20
20
|
#
|
21
|
-
# Operators will apply where clauses to query
|
21
|
+
# Operators will apply where clauses to query only if the filter_name is present in filters hash.
|
22
22
|
#
|
23
23
|
# [:equal_to]
|
24
|
-
# Applies field_name = filter_value
|
24
|
+
# Applies 'field_name = filter_value'
|
25
25
|
# [:contains]
|
26
26
|
# Applies substring (ILIKE '%filter_value%')
|
27
27
|
# [:greater_than_or_equal_to]
|
28
|
-
# Applies field_name >= filter_value
|
28
|
+
# Applies 'field_name >= filter_value'
|
29
29
|
# [:less_than]
|
30
|
-
# Applies field_name < filter_value
|
30
|
+
# Applies 'field_name < filter_value'
|
31
31
|
# [:present_if]
|
32
32
|
# Applies
|
33
|
-
#
|
34
|
-
#
|
33
|
+
# * 'field_name IS NOT NULL' if filter_value is truthy
|
34
|
+
# * 'field_name IS NULL' if filter_value is falsey
|
35
35
|
#
|
36
36
|
# === Examples
|
37
37
|
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
38
|
+
# query.filter_field :received
|
39
|
+
# query.filter_field :reference, contains: :reference
|
40
|
+
# query.filter_field [:vendor, :name], equal_to: :vendor_name
|
41
|
+
# query.filter_field [:receive_order_items, :sku, :code], equal_to: :sku_code
|
42
|
+
# query.filter_field :expected_delivery_at, greater_than_or_equal_to: :from_expected_delivery_at, less_than: :to_expected_delivery_at
|
43
|
+
# query.filter_field :expected_delivery_at, less_than_or_equal_to: :to_expected_delivery_at
|
44
|
+
# query.filter_field :outbound_trailer_id, present_if: :has_trailer
|
45
45
|
#
|
46
46
|
def filter_field(field_name, predicates={equal_to: field_name})
|
47
47
|
predicates.each do |operator, filter|
|
@@ -4,15 +4,15 @@ require 'integration_spec_helper'
|
|
4
4
|
RSpec.describe IndexQueryBuilder do
|
5
5
|
describe 'operator :contains' do
|
6
6
|
it 'returns records containing filter value' do
|
7
|
-
Post.create!(title: "
|
8
|
-
Post.create!(title: "
|
7
|
+
Post.create!(title: "This is a post I love")
|
8
|
+
Post.create!(title: "This is another post")
|
9
9
|
|
10
|
-
posts = IndexQueryBuilder.query Post.where(nil), with: { title: '
|
10
|
+
posts = IndexQueryBuilder.query Post.where(nil), with: { title: 'love' } do |query|
|
11
11
|
query.filter_field :title, contains: :title
|
12
12
|
end
|
13
13
|
|
14
14
|
expect(posts.length).to eq(1)
|
15
|
-
expect(posts[0].title).to eq("
|
15
|
+
expect(posts[0].title).to eq("This is a post I love")
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'filters using association' do
|
@@ -28,15 +28,15 @@ RSpec.describe IndexQueryBuilder do
|
|
28
28
|
|
29
29
|
describe 'operator :equal_to' do
|
30
30
|
it 'returns records equal to filter value' do
|
31
|
-
Post.create!(title: "
|
32
|
-
Post.create!(title: "
|
31
|
+
Post.create!(title: "This is a post I love")
|
32
|
+
Post.create!(title: "This is another post")
|
33
33
|
|
34
|
-
posts = IndexQueryBuilder.query Post.where(nil), with: { title: "
|
34
|
+
posts = IndexQueryBuilder.query Post.where(nil), with: { title: "This is a post I love" } do |query|
|
35
35
|
query.filter_field :title, equal_to: :title
|
36
36
|
end
|
37
37
|
|
38
38
|
expect(posts.length).to eq(1)
|
39
|
-
expect(posts[0].title).to eq("
|
39
|
+
expect(posts[0].title).to eq("This is a post I love")
|
40
40
|
end
|
41
41
|
|
42
42
|
it 'filters using association' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: index_query_builder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arturo Pie
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-12-
|
11
|
+
date: 2016-12-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -120,12 +120,14 @@ executables: []
|
|
120
120
|
extensions: []
|
121
121
|
extra_rdoc_files: []
|
122
122
|
files:
|
123
|
+
- ".editorconfig"
|
123
124
|
- ".gitignore"
|
125
|
+
- ".rspec"
|
124
126
|
- Gemfile
|
125
127
|
- Gemfile.lock
|
126
128
|
- README.md
|
127
129
|
- Rakefile
|
128
|
-
- config/database.yml
|
130
|
+
- config/database.yml.sample
|
129
131
|
- index_query_builder.gemspec
|
130
132
|
- lib/index_query_builder.rb
|
131
133
|
- lib/index_query_builder/query_builder.rb
|