sql-builder 0.1.0 → 0.2.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 +4 -4
- data/README.md +37 -13
- data/lib/sql-builder/builder.rb +70 -17
- data/lib/sql-builder/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 150ba78258f5b3cb152890b76982b01652a5a779bfd4aecfbb1591e0ab7820ad
|
4
|
+
data.tar.gz: 726c4b1b6b4d7a522d02848059285529c924ad6109b367bd2fe42cb77f97f26e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d912e64843705a78564aed481d87f1dd00959cd80b33583f7cd7b9cdb12b4cf7bf0693d3f9041972a0e00deeb79037905fef68d733aac1e20958e67119c8aca5
|
7
|
+
data.tar.gz: 338b7ae715a9c01144a228d5e752db1007fb21504463b58ff69fc9a5201a2d032b317f817b236f628adad540cea113b3bfb3143086ee485bf91c90b1798cf741
|
data/README.md
CHANGED
@@ -4,11 +4,13 @@ A simple SQL builder for generate SQL for non-ActiveRecord supports databases.
|
|
4
4
|
|
5
5
|
[](https://travis-ci.org/huacnlee/sql-builder)
|
6
6
|
|
7
|
+
[中文说明](https://ruby-china.org/topics/39399)
|
8
|
+
|
7
9
|
## Features
|
8
10
|
|
9
|
-
-
|
10
|
-
- Sanitize SQL by ActiveRecord methods, keep security.
|
11
|
-
-
|
11
|
+
- ActiveRecord style DSL.
|
12
|
+
- [Sanitize](https://api.rubyonrails.org/classes/ActiveRecord/Sanitization/ClassMethods.html#method-i-sanitize_sql) SQL by ActiveRecord methods, keep security.
|
13
|
+
- Support any SQL databases (MySQL, PostgreSQL, TiDB, Amazon Redshift...)
|
12
14
|
|
13
15
|
## Installation
|
14
16
|
|
@@ -20,7 +22,9 @@ gem 'sql-builder'
|
|
20
22
|
|
21
23
|
## Usage
|
22
24
|
|
23
|
-
|
25
|
+
More API documents, please visit [rdoc.info/gems/sql-builder](https://rdoc.info/gems/sql-builder).
|
26
|
+
|
27
|
+
```ruby
|
24
28
|
SQLBuilder.new("SELECT * FROM users")
|
25
29
|
.where("name = ?", "hello world")
|
26
30
|
.where("status != ?", 1)
|
@@ -33,31 +37,51 @@ SQLBuilder.new("SELECT * FROM users")
|
|
33
37
|
=> "SELECT * FROM users WHERE name = 'hello world' AND status != 1 ORDER BY created_at desc, id asc LIMIT 20 OFFSET 0"
|
34
38
|
```
|
35
39
|
|
36
|
-
More
|
40
|
+
### More complex case
|
37
41
|
|
38
|
-
```
|
42
|
+
```ruby
|
39
43
|
query = SQLBuilder.new("SELECT users.name, users.age, user_profiles.bio, user_profiles.avatar FROM users INNER JOIN user_profiles ON users.id = user_profiles.user_id")
|
44
|
+
```
|
40
45
|
|
41
|
-
|
46
|
+
Add the conditions by request params:
|
47
|
+
|
48
|
+
```ruby
|
42
49
|
query.where("age >= ?", params[:age]) unless params[:age].blank?
|
43
50
|
query.where("status = ?", params[:status]) unless params[:status].nil?
|
44
51
|
if params[:created_at_from] && params[:created_at_to]
|
45
52
|
query.where("created_at >= ? and created_at <= ?", params[:created_at_from], params[:created_at_to])
|
46
53
|
end
|
47
54
|
query.order("id desc").limit(100).to_sql
|
55
|
+
```
|
56
|
+
|
57
|
+
Returns string SQL:
|
48
58
|
|
49
|
-
|
59
|
+
```sql
|
60
|
+
SELECT users.name, users.age, user_profiles.bio, user_profiles.avatar FROM users
|
61
|
+
INNER JOIN user_profiles ON users.id = user_profiles.user_id
|
62
|
+
WHERE age >= 18 AND status = 3 AND created_at >= '2020-01-03 10:54:08 +0800' AND created_at <= '2020-01-03 10:54:08 +0800'
|
63
|
+
ORDER BY id desc LIMIT 100 OFFSET 0
|
50
64
|
```
|
51
65
|
|
52
|
-
|
66
|
+
### Group by, Having
|
67
|
+
|
68
|
+
```rb
|
69
|
+
query = SQLBuilder.new("select user_id, name, count(ip) as ip_count from user_visits")
|
70
|
+
query.where("status = ?", 1).where("created_at > ?", params[:created_at])
|
71
|
+
query.group("user_id").group("name").having("count(ip) > 2")
|
72
|
+
query.to_sql
|
73
|
+
```
|
53
74
|
|
54
|
-
|
75
|
+
returns
|
55
76
|
|
56
|
-
|
77
|
+
```sql
|
78
|
+
select user_id, name, count(ip) as ip_count from user_visits WHERE status = 1 AND status = 1 AND created_at > '2020-01-03 10:54:08 +0800' GROUP BY user_id, name HAVING count(ip) > 2"
|
79
|
+
```
|
57
80
|
|
58
|
-
|
81
|
+
## TODO
|
59
82
|
|
60
|
-
|
83
|
+
- [ ] `OR` conditions;
|
84
|
+
- [X] `Group By`, `Having`;
|
61
85
|
|
62
86
|
## License
|
63
87
|
|
data/lib/sql-builder/builder.rb
CHANGED
@@ -1,33 +1,43 @@
|
|
1
|
+
require "active_record"
|
2
|
+
|
1
3
|
# SQLBuilder write the complex SQL as DSL
|
4
|
+
# = Example:
|
2
5
|
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# query = SQLBuilder.new("SELECT * FROM users inner join ")
|
6
|
+
# query = SQLBuilder.new("SELECT * FROM users")
|
6
7
|
# .where("name = ?", "hello world")
|
7
8
|
# .where("status != ?", 1)
|
8
9
|
# .order("created_at desc")
|
9
10
|
# .order("id asc")
|
10
11
|
# .page(1).per(20)
|
11
12
|
# .to_sql
|
12
|
-
require "active_record"
|
13
|
-
|
14
13
|
class SQLBuilder
|
15
|
-
attr_reader :sql, :conditions, :orders, :limit_options, :page_options
|
14
|
+
attr_reader :sql, :conditions, :havings, :orders, :groups, :limit_options, :page_options
|
16
15
|
delegate :sanitize_sql, :sanitize_sql_for_order, to: ActiveRecord::Base
|
17
16
|
|
17
|
+
# Create a new SQLBuilder
|
18
|
+
#
|
19
|
+
# == Example
|
20
|
+
# query = SQLBuilder.new("SELECT users.*, user_profiles.avatar FROM users INNER JOIN user_profiles ON users.id = user_profiles.id")
|
21
|
+
# query.to_sql
|
22
|
+
# # => "SELECT users.*, user_profiles.avatar FROM users INNER JOIN user_profiles ON users.id = user_profiles.id"
|
23
|
+
#
|
18
24
|
def initialize(sql = "")
|
19
25
|
@sql = sql
|
20
26
|
@conditions = []
|
21
27
|
@orders = []
|
28
|
+
@groups = []
|
29
|
+
@havings = []
|
22
30
|
@limit_options = {}
|
23
31
|
@page_options = { per_page: 20 }
|
24
32
|
end
|
25
33
|
|
26
34
|
# Add `AND` condition
|
27
35
|
#
|
28
|
-
#
|
36
|
+
# query.where("name = ?", params[:name]).where("age >= ?", 18)
|
37
|
+
#
|
29
38
|
# or
|
30
|
-
#
|
39
|
+
#
|
40
|
+
# count_query.where(query)
|
31
41
|
def where(*condition)
|
32
42
|
case condition.first
|
33
43
|
when SQLBuilder
|
@@ -42,16 +52,15 @@ class SQLBuilder
|
|
42
52
|
|
43
53
|
# Order By
|
44
54
|
#
|
45
|
-
#
|
46
|
-
# => "ORDER BY name asc, created_at desc"
|
55
|
+
# query.order("name asc").order("created_at desc").to_sql
|
56
|
+
# # => "ORDER BY name asc, created_at desc"
|
47
57
|
def order(condition)
|
48
58
|
orders << sanitize_sql_for_order(condition)
|
49
59
|
self
|
50
60
|
end
|
51
61
|
|
52
62
|
# Offset
|
53
|
-
#
|
54
|
-
# query.offset(3).limit(10) => "LIMIT 10 OFFSET 3"
|
63
|
+
# See #limit
|
55
64
|
def offset(offset)
|
56
65
|
limit_options[:offset] = offset.to_i
|
57
66
|
self
|
@@ -59,17 +68,54 @@ class SQLBuilder
|
|
59
68
|
|
60
69
|
# Limit
|
61
70
|
#
|
62
|
-
#
|
71
|
+
# query.offset(3).limit(10).to_sql
|
72
|
+
# # => "LIMIT 10 OFFSET 3"
|
63
73
|
def limit(limit)
|
64
74
|
limit_options[:offset] ||= 0
|
65
75
|
limit_options[:limit] = limit.to_i
|
66
76
|
self
|
67
77
|
end
|
68
78
|
|
79
|
+
# Group By
|
80
|
+
#
|
81
|
+
# Allows to specify a group attribute:
|
82
|
+
#
|
83
|
+
# query.group("name as new_name, age").to_sql
|
84
|
+
# # => "GROUP BY name as new_name, age"
|
85
|
+
#
|
86
|
+
# or
|
87
|
+
#
|
88
|
+
# query.group("name", "age").to_sql # => "GROUP BY name, age"
|
89
|
+
# query.group(:name, :age).to_sql # => "GROUP BY name, age"
|
90
|
+
# query.group(["name", "age"]).to_sql # => "GROUP BY name, age"
|
91
|
+
# query.group("name").group("age").to_sql # => "GROUP BY name, age"
|
92
|
+
#
|
93
|
+
def group(*args)
|
94
|
+
case args.first
|
95
|
+
when Array
|
96
|
+
@groups += args.first.collect(&:to_s)
|
97
|
+
else
|
98
|
+
@groups += args.collect(&:to_s)
|
99
|
+
end
|
100
|
+
|
101
|
+
@groups.uniq!
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
# Having
|
106
|
+
#
|
107
|
+
# query.group("name").having("count(name) > ?", 5).to_sql
|
108
|
+
# # => "GROUP BY name HAVING count(name) > 5"
|
109
|
+
#
|
110
|
+
def having(*condition)
|
111
|
+
havings << sanitize_sql(condition)
|
112
|
+
self
|
113
|
+
end
|
114
|
+
|
69
115
|
# Pagination
|
70
116
|
#
|
71
|
-
#
|
72
|
-
#
|
117
|
+
# query.page(1).per(12).to_sql # => "LIMIT 12 OFFSET 0"
|
118
|
+
# query.page(2).per(12).to_sql # => "LIMIT 12 OFFSET 12"
|
73
119
|
def page(page_no)
|
74
120
|
page_options[:page] = page_no
|
75
121
|
page_options[:per_page] ||= 10
|
@@ -79,7 +125,8 @@ class SQLBuilder
|
|
79
125
|
self
|
80
126
|
end
|
81
127
|
|
82
|
-
#
|
128
|
+
# Set per_page limit
|
129
|
+
# See #page
|
83
130
|
def per(per_page)
|
84
131
|
page_options[:per_page] = per_page
|
85
132
|
self.page(page_options[:page])
|
@@ -95,6 +142,12 @@ class SQLBuilder
|
|
95
142
|
if orders.any?
|
96
143
|
sql_parts << "ORDER BY " + orders.join(", ")
|
97
144
|
end
|
145
|
+
if groups.any?
|
146
|
+
sql_parts << "GROUP BY " + groups.join(", ")
|
147
|
+
end
|
148
|
+
if havings.any?
|
149
|
+
sql_parts << "HAVING " + havings.join(" AND ")
|
150
|
+
end
|
98
151
|
if limit_options[:limit]
|
99
152
|
sql_parts << "LIMIT " + limit_options[:limit].to_s
|
100
153
|
end
|
@@ -103,4 +156,4 @@ class SQLBuilder
|
|
103
156
|
end
|
104
157
|
sql_parts.join(" ")
|
105
158
|
end
|
106
|
-
end
|
159
|
+
end
|
data/lib/sql-builder/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sql-builder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Lee
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-01-
|
11
|
+
date: 2020-01-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|