querylet-rails 1.0.0
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 +7 -0
- data/Readme.md +101 -0
- data/lib/querylet_rails/controller/queryable.rb +61 -0
- data/lib/querylet_rails/model/queryable.rb +119 -0
- data/lib/querylet_rails/version.rb +3 -0
- metadata +89 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 66c6879ef73a6a30386db584b9237cfbcc1fe3eb6c75b0807ab91a43f5272714
|
|
4
|
+
data.tar.gz: c2bf111d8e123d42557a69f422dc97c90950c55393df92235c6dc971e18914c1
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 817d3712b2f5b175a974389ece5dadec2e84b286288cf91d9ff5e06100189c5d39b925a208a203463a69f5de5854b991a25ae47bb8982e402dcea3a04a80173d
|
|
7
|
+
data.tar.gz: 52499b2461bf2090b7b9e03c6ad98101d6806fb6e1526bd64a884deca3043eb3413fbb22a29eafa548d71080acb61705b80ee1727c7745c5c953dd0b8bcda263
|
data/Readme.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Querylet Rails
|
|
2
|
+
|
|
3
|
+
## What is Querylet?
|
|
4
|
+
|
|
5
|
+
[Querylet](https://github.com/teacherseat/querylet) is a query template
|
|
6
|
+
langauge for Postgres to ease working with complex queries eg.
|
|
7
|
+
|
|
8
|
+
Similar to handlebars but a much simpler langague and specfically
|
|
9
|
+
designed for postgre queries.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
{{#if count}}
|
|
13
|
+
{{> include 'admin.shared.paginate_select'}}
|
|
14
|
+
{{/else}}
|
|
15
|
+
{{> include 'admin.users.select'}}
|
|
16
|
+
{{/if}}
|
|
17
|
+
FROM users
|
|
18
|
+
WHERE true
|
|
19
|
+
AND 'customer' != ANY(users.role)
|
|
20
|
+
{{#if search}}
|
|
21
|
+
AND (
|
|
22
|
+
(coalesce(users.first_name, '') || ' ' || coalesce(users.last_name, '') ILIKE {{wild search}})
|
|
23
|
+
OR users.email ILIKE {{wild search}}
|
|
24
|
+
)
|
|
25
|
+
{{/if}}
|
|
26
|
+
{{#unless count}}
|
|
27
|
+
{{#if sort}}
|
|
28
|
+
ORDER BY {{sort}}
|
|
29
|
+
{{/else}}
|
|
30
|
+
ORDER BY users.id ASC
|
|
31
|
+
{{/if}}
|
|
32
|
+
{{> include 'admin.shared.paginate_offset'}}
|
|
33
|
+
{{/unless}}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## What is Querylet Rails?
|
|
37
|
+
|
|
38
|
+
Querylet Rails helps configure querylet for use in a Rails application.
|
|
39
|
+
|
|
40
|
+
It contains two files:
|
|
41
|
+
|
|
42
|
+
- querylet_rails/controller/queryable.rb (QueryletRails::Controller::Queryable)
|
|
43
|
+
- querylet_rails/model/queryable.rb (QueryletRails::Model::Queryable)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
## QueryletRails::Controller::Queryable
|
|
47
|
+
|
|
48
|
+
This module is a Rails Controller Concerns to include your ApplicationController
|
|
49
|
+
|
|
50
|
+
It defines the following controller methods:
|
|
51
|
+
|
|
52
|
+
- set_pagination_headers
|
|
53
|
+
- render_paginated
|
|
54
|
+
- render_paginated_json
|
|
55
|
+
- index_params
|
|
56
|
+
|
|
57
|
+
## QueryletRails::Model::Queryable
|
|
58
|
+
|
|
59
|
+
This module is a Rails Model Concerns to include your ApplicationRecord
|
|
60
|
+
|
|
61
|
+
It defines the following model methods:
|
|
62
|
+
|
|
63
|
+
- select_value and self.select_value
|
|
64
|
+
- select_values and self.select_values
|
|
65
|
+
- select_object and self.select_object
|
|
66
|
+
- select_array and self.select_array
|
|
67
|
+
- select_all and self.select_all
|
|
68
|
+
- select_paginate and self.select_paginate
|
|
69
|
+
- self.query_root
|
|
70
|
+
- self.query
|
|
71
|
+
- self.query_compile_template
|
|
72
|
+
- self.query_wrap_object
|
|
73
|
+
- self.query_wrap_array
|
|
74
|
+
|
|
75
|
+
## How to Install
|
|
76
|
+
|
|
77
|
+
Create a queries directory eg.
|
|
78
|
+
|
|
79
|
+
Create a directory to contain your queries
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
mkdir app/queries
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Include `QueryletRails::Controller::Queryable` in your ApplicationController
|
|
86
|
+
|
|
87
|
+
```rb
|
|
88
|
+
require 'querylet_rails/controller/queryable'
|
|
89
|
+
class ApplicationController < ActionController::Base
|
|
90
|
+
include QueryletRails::Controller::Queryable
|
|
91
|
+
end
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Include `QueryletRails::Model::Queryablek` in your ApplicationRecord
|
|
95
|
+
|
|
96
|
+
```rb
|
|
97
|
+
require 'querylet_rails/model/queryable'
|
|
98
|
+
class ApplicationRecord < ActiveRecord::Base
|
|
99
|
+
include QueryletRails::Model::Queryable
|
|
100
|
+
end
|
|
101
|
+
```
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module QueryletRails
|
|
2
|
+
module Controller
|
|
3
|
+
module Queryable
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
def set_pagination_headers count_json
|
|
7
|
+
json = JSON.parse(count_json)
|
|
8
|
+
json = json.first if json.is_a?(Array)
|
|
9
|
+
page = json['page']
|
|
10
|
+
total = json['total_entries']
|
|
11
|
+
per_page = json['per_page'] || 20
|
|
12
|
+
pages = (total.to_f / per_page).ceil
|
|
13
|
+
end_entry = page * per_page
|
|
14
|
+
end_entry = total if end_entry > total
|
|
15
|
+
headers["X-Pagination"] = {
|
|
16
|
+
page: page,
|
|
17
|
+
total: total,
|
|
18
|
+
total_pages: pages,
|
|
19
|
+
first_page: page == 1,
|
|
20
|
+
last_page: page >= pages,
|
|
21
|
+
previous_page: page - 1,
|
|
22
|
+
next_page: page + 1,
|
|
23
|
+
out_of_bounds: page < 1 || page > pages,
|
|
24
|
+
first_entry: (page - 1) * per_page + 1,
|
|
25
|
+
end_entry: end_entry
|
|
26
|
+
}.to_json
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# AB - We should phase out {{sort}} in favour
|
|
30
|
+
# of {{order}} {{by}} for more finetune control
|
|
31
|
+
def render_paginated target, method, attrs
|
|
32
|
+
if attrs.key?(:sort)
|
|
33
|
+
name,dir = attrs[:sort].split ','
|
|
34
|
+
v = [name]
|
|
35
|
+
v.push dir.upcase if dir
|
|
36
|
+
v = v.join ' '
|
|
37
|
+
attrs[:sort] = v
|
|
38
|
+
attrs[:order] = name
|
|
39
|
+
attrs[:by] = (dir || 'ASC').upcase
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Use the same query but to count all the records
|
|
43
|
+
attrs[:count] = true
|
|
44
|
+
count_json = target.send method, attrs
|
|
45
|
+
set_pagination_headers count_json
|
|
46
|
+
attrs.delete(:count)
|
|
47
|
+
|
|
48
|
+
target.send method, attrs
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def render_paginated_json target, method, attrs
|
|
52
|
+
json = render_paginated target, method, attrs
|
|
53
|
+
render json: json
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def index_params
|
|
57
|
+
params.permit :page, :search, :sort
|
|
58
|
+
end
|
|
59
|
+
end # Queryable
|
|
60
|
+
end # Controller
|
|
61
|
+
end # QueryletRails
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
require 'querylet'
|
|
2
|
+
|
|
3
|
+
module QueryletRails
|
|
4
|
+
module Model
|
|
5
|
+
module Queryable
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
# override this function
|
|
8
|
+
|
|
9
|
+
def select_value relative_path, data={}
|
|
10
|
+
self.class.select_value relative_path, data
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def select_values relative_path, data={}
|
|
14
|
+
self.class.select_values relative_path, data
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def select_object relative_path, data={}
|
|
18
|
+
self.class.select_object relative_path, data
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def select_array relative_path, data={}
|
|
22
|
+
self.class.select_array relative_path, data
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def select_all relative_path, data={}
|
|
26
|
+
self.class.select_all relative_path, data
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def select_paginate query, attrs, count
|
|
30
|
+
self.class.select_paginate query, attrs, count
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
module ClassMethods
|
|
34
|
+
def query_root
|
|
35
|
+
Rails.root
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def query relative_path, data={}
|
|
39
|
+
file_path = self.query_root.join('app','queries',relative_path + '.sql')
|
|
40
|
+
template = File.read file_path
|
|
41
|
+
query_compile_template template, data
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def query_compile_template template, data={}
|
|
45
|
+
querylet = Querylet::Querylet.new path: self.query_root.join('app','queries').to_s
|
|
46
|
+
begin
|
|
47
|
+
querylet.compile(template).call(data)
|
|
48
|
+
rescue => e
|
|
49
|
+
puts ""
|
|
50
|
+
puts "===== Querylet Compile Error Occured ====="
|
|
51
|
+
template_annotated = ''
|
|
52
|
+
template.split("\n").each_with_index do |line,i|
|
|
53
|
+
template_annotated << "#{(i+1).to_s.rjust(3, " ")} | #{line}\n"
|
|
54
|
+
end
|
|
55
|
+
puts template_annotated
|
|
56
|
+
raise e
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def select_value relative_path, data={}
|
|
61
|
+
sql = self.query relative_path, data
|
|
62
|
+
self.connection.select_value sql
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def select_values relative_path, data={}
|
|
66
|
+
sql = self.query relative_path, data
|
|
67
|
+
self.connection.select_values sql
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def select_array relative_path, data={}
|
|
71
|
+
template = self.query relative_path, data
|
|
72
|
+
sql = query_wrap_array template
|
|
73
|
+
self.connection.select_value sql
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def select_object relative_path, data={}
|
|
77
|
+
template = self.query relative_path, data
|
|
78
|
+
sql = query_wrap_object template
|
|
79
|
+
self.connection.select_value sql
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def select_all relative_path, data={}
|
|
83
|
+
sql = self.query relative_path, data
|
|
84
|
+
self.connection.select_all sql
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def query_wrap_object template
|
|
88
|
+
<<-HEREDOC.chomp
|
|
89
|
+
(SELECT COALESCE(row_to_json(object_row),'{}'::json) FROM (
|
|
90
|
+
#{template.to_s.chomp}
|
|
91
|
+
) object_row)
|
|
92
|
+
HEREDOC
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def query_wrap_array template
|
|
96
|
+
<<-HEREDOC.chomp
|
|
97
|
+
(SELECT COALESCE(array_to_json(array_agg(row_to_json(array_row))),'[]'::json) FROM (
|
|
98
|
+
#{template.to_s.chomp}
|
|
99
|
+
) array_row)
|
|
100
|
+
HEREDOC
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def select_paginate relative_path, data, count
|
|
104
|
+
file_path = self.query_root.join('app','queries',relative_path + '.sql')
|
|
105
|
+
template = File.read file_path
|
|
106
|
+
if count
|
|
107
|
+
sql = self.query_wrap_object template
|
|
108
|
+
sql = self.query_compile_template sql, data
|
|
109
|
+
self.connection.select_value sql
|
|
110
|
+
else
|
|
111
|
+
sql = self.query_wrap_array template
|
|
112
|
+
sql = self.query_compile_template sql, data
|
|
113
|
+
self.connection.select_value sql
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end # Queryable
|
|
118
|
+
end # Model
|
|
119
|
+
end # QueryletRails
|
metadata
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: querylet-rails
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- TeacherSeat
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2020-12-12 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: querylet
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: pry
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rspec
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
description: Querylet Rails
|
|
56
|
+
email:
|
|
57
|
+
- andrew@teacherseat.com
|
|
58
|
+
executables: []
|
|
59
|
+
extensions: []
|
|
60
|
+
extra_rdoc_files: []
|
|
61
|
+
files:
|
|
62
|
+
- Readme.md
|
|
63
|
+
- lib/querylet_rails/controller/queryable.rb
|
|
64
|
+
- lib/querylet_rails/model/queryable.rb
|
|
65
|
+
- lib/querylet_rails/version.rb
|
|
66
|
+
homepage: https://teacherseat.com
|
|
67
|
+
licenses:
|
|
68
|
+
- MIT
|
|
69
|
+
metadata: {}
|
|
70
|
+
post_install_message:
|
|
71
|
+
rdoc_options: []
|
|
72
|
+
require_paths:
|
|
73
|
+
- lib
|
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
75
|
+
requirements:
|
|
76
|
+
- - ">="
|
|
77
|
+
- !ruby/object:Gem::Version
|
|
78
|
+
version: '0'
|
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
80
|
+
requirements:
|
|
81
|
+
- - ">="
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: '0'
|
|
84
|
+
requirements: []
|
|
85
|
+
rubygems_version: 3.1.2
|
|
86
|
+
signing_key:
|
|
87
|
+
specification_version: 4
|
|
88
|
+
summary: Querylet Rails
|
|
89
|
+
test_files: []
|