database-core 0.1.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/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +116 -0
- data/Rakefile +4 -0
- data/lib/database/v1/query.rb +144 -0
- data/lib/database/v1/query_runner.rb +157 -0
- data/lib/database/v2/query.rb +121 -0
- data/lib/database/v2/query_runner.rb +100 -0
- data/lib/database/v2/wherer.rb +62 -0
- data/lib/database/v3/delete_model.rb +18 -0
- data/lib/database/v3/delete_runner.rb +20 -0
- data/lib/database/v3/insert_model.rb +33 -0
- data/lib/database/v3/insert_runner.rb +20 -0
- data/lib/database/v3/policy_model.rb +29 -0
- data/lib/database/v3/policy_runner.rb +23 -0
- data/lib/database/v3/query_model.rb +126 -0
- data/lib/database/v3/query_runner.rb +163 -0
- data/lib/database/v3/sanitize.rb +19 -0
- data/lib/database/v3/update_model.rb +30 -0
- data/lib/database/v3/update_runner.rb +20 -0
- data/lib/database/v3/where_model.rb +144 -0
- data/lib/database/version.rb +5 -0
- data/lib/database.rb +7 -0
- data/sig/database.rbs +4 -0
- metadata +87 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
module Database::V3
|
|
2
|
+
|
|
3
|
+
class QueryRunner
|
|
4
|
+
|
|
5
|
+
def self.query models_query
|
|
6
|
+
|
|
7
|
+
models_values = setup_query(models_query)
|
|
8
|
+
|
|
9
|
+
remove_keys(models_query, models_values)
|
|
10
|
+
|
|
11
|
+
models_values = setup_values(models_values)
|
|
12
|
+
|
|
13
|
+
models_values
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.setup_query models_query
|
|
17
|
+
|
|
18
|
+
models_values = { "rows" => {} }
|
|
19
|
+
|
|
20
|
+
models_query.each do |model, query|
|
|
21
|
+
|
|
22
|
+
QueryModel.build_parents(model, query)
|
|
23
|
+
QueryModel.build_children(model, query)
|
|
24
|
+
|
|
25
|
+
sql = QueryModel.build(model, query)
|
|
26
|
+
|
|
27
|
+
values = ActiveRecord::Base.connection.exec_query(sql).map(&:as_json)
|
|
28
|
+
|
|
29
|
+
if query["rows"]
|
|
30
|
+
|
|
31
|
+
sql = QueryModel.build_count(model, query)
|
|
32
|
+
|
|
33
|
+
rows = ActiveRecord::Base.connection.exec_query(sql).first.as_json
|
|
34
|
+
|
|
35
|
+
models_values["rows"].merge!(rows)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
if values.present?
|
|
39
|
+
|
|
40
|
+
setup_relation values, query["parents"], true
|
|
41
|
+
setup_relation values, query["children"], false
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
models_values[model] = values
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
models_values
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.setup_relation parents, models_query, relation
|
|
51
|
+
|
|
52
|
+
return unless models_query.present?
|
|
53
|
+
|
|
54
|
+
setup_relation_query(parents, models_query, relation)
|
|
55
|
+
setup_relation_value(parents, models_query, relation)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.setup_relation_query parents, models_query, relation
|
|
59
|
+
|
|
60
|
+
models_query.each do |model, query|
|
|
61
|
+
|
|
62
|
+
next if query["key"].nil?
|
|
63
|
+
|
|
64
|
+
foreign_key = relation ? "id" : query["key"]
|
|
65
|
+
primary_key = relation ? query["key"] : "id"
|
|
66
|
+
|
|
67
|
+
keys = parents.map{ |parent| parent[primary_key] }.uniq.compact
|
|
68
|
+
|
|
69
|
+
next if keys.empty?
|
|
70
|
+
|
|
71
|
+
query["and"] = {} if query["and"].nil?
|
|
72
|
+
|
|
73
|
+
if query["and"][foreign_key]
|
|
74
|
+
query["and"][foreign_key]["in"] ?
|
|
75
|
+
query["and"][foreign_key]["in"] << keys :
|
|
76
|
+
query["and"][foreign_key]["in"] = keys
|
|
77
|
+
else
|
|
78
|
+
query["and"][foreign_key] = { "in" => keys }
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def self.setup_relation_value parents, models_query, relation
|
|
84
|
+
|
|
85
|
+
models_values = setup_query(models_query)
|
|
86
|
+
|
|
87
|
+
parents.each do |parent|
|
|
88
|
+
|
|
89
|
+
models_query.each do |model, query|
|
|
90
|
+
|
|
91
|
+
next if query["key"].nil?
|
|
92
|
+
|
|
93
|
+
foreign_key = relation ? "id" : query["key"]
|
|
94
|
+
primary_key = relation ? query["key"] : "id"
|
|
95
|
+
|
|
96
|
+
values = models_values[model].select{ |value| value[foreign_key] == parent[primary_key] }
|
|
97
|
+
|
|
98
|
+
parent[model] = relation ? values.first : values
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def self.setup_values target
|
|
104
|
+
|
|
105
|
+
if target.is_a?(Integer)
|
|
106
|
+
return target > 2**32 ? target.to_s : target
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
if target.is_a?(Array)
|
|
110
|
+
return target.map { |value| setup_values(value) }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
if target.is_a?(Hash)
|
|
114
|
+
return target.transform_values { |value| setup_values(value) }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
if target.is_a?(String)
|
|
118
|
+
unless [Encoding::US_ASCII, Encoding::UTF_8].include?(target.encoding)
|
|
119
|
+
return target.bytes.to_a
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
target
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def self.remove_keys models_query, models_values
|
|
127
|
+
|
|
128
|
+
models_query&.each do |model, query|
|
|
129
|
+
|
|
130
|
+
if models_values[model].is_a?(Array)
|
|
131
|
+
models_values[model].each do |value|
|
|
132
|
+
remove_key query, value
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
if models_values[model].is_a?(Hash)
|
|
137
|
+
value = models_values[model]
|
|
138
|
+
remove_key query, value
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def self.remove_key query, value
|
|
144
|
+
|
|
145
|
+
columns = query["columns"]&.map{ |column| column.is_a?(Hash) ? column["alias"] : column }
|
|
146
|
+
|
|
147
|
+
value.keys.each do |key|
|
|
148
|
+
|
|
149
|
+
next if columns.include?(key)
|
|
150
|
+
|
|
151
|
+
next if query["parents"]&.key?(key)
|
|
152
|
+
next if query["children"]&.key?(key)
|
|
153
|
+
|
|
154
|
+
value.delete(key)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
remove_keys query["parents"], value
|
|
158
|
+
remove_keys query["children"], value
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Database::V3
|
|
2
|
+
|
|
3
|
+
RULE = /[^a-zA-Z0-9_]/.freeze
|
|
4
|
+
|
|
5
|
+
class Sanitize
|
|
6
|
+
|
|
7
|
+
def self.target params
|
|
8
|
+
|
|
9
|
+
params.to_s.gsub(RULE, "")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.array params
|
|
13
|
+
|
|
14
|
+
ActiveRecord::Base.sanitize_sql_array(params)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Database::V3
|
|
2
|
+
|
|
3
|
+
class UpdateModel
|
|
4
|
+
|
|
5
|
+
def self.build model, params
|
|
6
|
+
|
|
7
|
+
model = Sanitize.target(model)
|
|
8
|
+
|
|
9
|
+
columns = build_columns(params).join(", ")
|
|
10
|
+
|
|
11
|
+
output = ["UPDATE `#{model}` SET #{columns}"]
|
|
12
|
+
|
|
13
|
+
WhereModel.build(output, params)
|
|
14
|
+
|
|
15
|
+
output.join(" ")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.build_columns params
|
|
19
|
+
|
|
20
|
+
params["columns"].map do |column, value|
|
|
21
|
+
|
|
22
|
+
column = Sanitize.target(column)
|
|
23
|
+
|
|
24
|
+
Sanitize.array(["`#{column}` = ?", value])
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Database::V3
|
|
2
|
+
|
|
3
|
+
class UpdateRunner
|
|
4
|
+
|
|
5
|
+
def self.update input
|
|
6
|
+
|
|
7
|
+
input.each do |model, payload|
|
|
8
|
+
|
|
9
|
+
Array.wrap(payload).each do |item|
|
|
10
|
+
|
|
11
|
+
sql = UpdateModel.build(model, item)
|
|
12
|
+
|
|
13
|
+
ActiveRecord::Base.connection.execute(sql)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
module Database::V3
|
|
2
|
+
|
|
3
|
+
class WhereModel
|
|
4
|
+
|
|
5
|
+
OPERATORS = {
|
|
6
|
+
|
|
7
|
+
"=" => "=",
|
|
8
|
+
"!=" => "!=",
|
|
9
|
+
"<>" => "<>",
|
|
10
|
+
">" => ">",
|
|
11
|
+
"<" => "<",
|
|
12
|
+
">=" => ">=",
|
|
13
|
+
"<=" => "<=",
|
|
14
|
+
"in" => "IN",
|
|
15
|
+
"like" => "LIKE",
|
|
16
|
+
"!in" => "NOT IN",
|
|
17
|
+
"!like" => "NOT LIKE",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
def self.build output, query
|
|
21
|
+
|
|
22
|
+
where = []
|
|
23
|
+
|
|
24
|
+
where << query["and"].to_h.map{ |key, value| build_scopes(" AND ", key, value) }.compact_blank.join(" AND ")
|
|
25
|
+
|
|
26
|
+
where << query["or"].to_h.map{ |key, value| build_scopes(" OR ", key, value) }.compact_blank.join(" OR ")
|
|
27
|
+
|
|
28
|
+
where = where.compact_blank.join(" AND ")
|
|
29
|
+
|
|
30
|
+
return unless where.present?
|
|
31
|
+
|
|
32
|
+
output << "WHERE #{where}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.build_scopes scope, column, rules
|
|
36
|
+
|
|
37
|
+
if rules.is_a? FalseClass
|
|
38
|
+
|
|
39
|
+
return build_rules(column, "=", rules)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
if rules.is_a? TrueClass
|
|
43
|
+
|
|
44
|
+
return build_rules(column, "=", rules)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if rules.is_a? Numeric
|
|
48
|
+
|
|
49
|
+
return build_rules(column, "=", rules)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
if rules.is_a? String
|
|
53
|
+
|
|
54
|
+
return build_rules(column, "contains", rules)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
output = build_scope(scope, column, rules)
|
|
58
|
+
|
|
59
|
+
output.present? ? "(#{output})" : ""
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.build_scope scope, column, rules
|
|
63
|
+
|
|
64
|
+
case column
|
|
65
|
+
when "and"
|
|
66
|
+
return rules.map{ |key, value| build_scopes(" AND ", key, value) }.compact_blank.join(" AND ")
|
|
67
|
+
when "or"
|
|
68
|
+
return rules.map{ |key, value| build_scopes(" OR ", key, value) }.compact_blank.join(" OR ")
|
|
69
|
+
else
|
|
70
|
+
return rules.map{ |key, value| build_rules(column, key, value) }.compact_blank.join(scope)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def self.build_rules column, operator, value
|
|
75
|
+
|
|
76
|
+
case operator
|
|
77
|
+
when "and"
|
|
78
|
+
return build_scopes(" AND ", column, value)
|
|
79
|
+
when "or"
|
|
80
|
+
return build_scopes(" OR ", column, value)
|
|
81
|
+
else
|
|
82
|
+
return build_rule(column, operator, value)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def self.build_rule column, operator, value
|
|
87
|
+
|
|
88
|
+
return "" if value.is_a? NilClass
|
|
89
|
+
|
|
90
|
+
column = Sanitize.target(column)
|
|
91
|
+
|
|
92
|
+
if operator == "null"
|
|
93
|
+
|
|
94
|
+
value = value ? " " : " NOT "
|
|
95
|
+
|
|
96
|
+
return "`#{column}` IS#{value}NULL"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
if operator == "last"
|
|
100
|
+
return Sanitize.array(["`#{column}` LIKE ?", "%#{value}"])
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
if operator == "first"
|
|
104
|
+
return Sanitize.array(["`#{column}` LIKE ?", "#{value}%"])
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
if operator == "contains"
|
|
108
|
+
return Sanitize.array(["`#{column}` LIKE ?", "%#{value}%"])
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
operator = get_operator(operator)
|
|
112
|
+
|
|
113
|
+
if value.is_a? Numeric
|
|
114
|
+
return Sanitize.array(["`#{column}` #{operator} ?", value])
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
if value.is_a? String
|
|
118
|
+
return Sanitize.array(["`#{column}` #{operator} ?", value])
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
if value.is_a? Array
|
|
122
|
+
return Sanitize.array(["`#{column}` #{operator} (?)", value])
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
if value.is_a? Hash
|
|
126
|
+
|
|
127
|
+
value = value.map{ |model, query| QueryModel.build(model, query) }
|
|
128
|
+
|
|
129
|
+
value = value.map{ |value| "(#{value})" }.join(" UNION ")
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
"`#{column}` #{operator} #{value}"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def self.get_operator operator
|
|
136
|
+
|
|
137
|
+
return OPERATORS[operator] if OPERATORS.key?(operator)
|
|
138
|
+
|
|
139
|
+
raise KeyError, operator
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
end
|
data/lib/database.rb
ADDED
data/sig/database.rbs
ADDED
metadata
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: database-core
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Tiago da Silva
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-01-01 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activerecord
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '7.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '7.0'
|
|
27
|
+
description: A comprehensive Ruby library providing database abstraction and management
|
|
28
|
+
features, making it easier to interact with various databases.
|
|
29
|
+
email:
|
|
30
|
+
- tyagoy@gmail.com
|
|
31
|
+
executables: []
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- CHANGELOG.md
|
|
36
|
+
- CODE_OF_CONDUCT.md
|
|
37
|
+
- LICENSE.txt
|
|
38
|
+
- README.md
|
|
39
|
+
- Rakefile
|
|
40
|
+
- lib/database.rb
|
|
41
|
+
- lib/database/v1/query.rb
|
|
42
|
+
- lib/database/v1/query_runner.rb
|
|
43
|
+
- lib/database/v2/query.rb
|
|
44
|
+
- lib/database/v2/query_runner.rb
|
|
45
|
+
- lib/database/v2/wherer.rb
|
|
46
|
+
- lib/database/v3/delete_model.rb
|
|
47
|
+
- lib/database/v3/delete_runner.rb
|
|
48
|
+
- lib/database/v3/insert_model.rb
|
|
49
|
+
- lib/database/v3/insert_runner.rb
|
|
50
|
+
- lib/database/v3/policy_model.rb
|
|
51
|
+
- lib/database/v3/policy_runner.rb
|
|
52
|
+
- lib/database/v3/query_model.rb
|
|
53
|
+
- lib/database/v3/query_runner.rb
|
|
54
|
+
- lib/database/v3/sanitize.rb
|
|
55
|
+
- lib/database/v3/update_model.rb
|
|
56
|
+
- lib/database/v3/update_runner.rb
|
|
57
|
+
- lib/database/v3/where_model.rb
|
|
58
|
+
- lib/database/version.rb
|
|
59
|
+
- sig/database.rbs
|
|
60
|
+
homepage: https://rubygems.org/gems/database
|
|
61
|
+
licenses:
|
|
62
|
+
- MIT
|
|
63
|
+
metadata:
|
|
64
|
+
allowed_push_host: https://rubygems.org
|
|
65
|
+
homepage_uri: https://rubygems.org/gems/database
|
|
66
|
+
source_code_uri: https://github.com/tyagoy/database-core
|
|
67
|
+
changelog_uri: https://github.com/tyagoy/database-core/blob/main/CHANGELOG.md
|
|
68
|
+
post_install_message:
|
|
69
|
+
rdoc_options: []
|
|
70
|
+
require_paths:
|
|
71
|
+
- lib
|
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
73
|
+
requirements:
|
|
74
|
+
- - ">="
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: 3.2.0
|
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '0'
|
|
82
|
+
requirements: []
|
|
83
|
+
rubygems_version: 3.4.10
|
|
84
|
+
signing_key:
|
|
85
|
+
specification_version: 4
|
|
86
|
+
summary: A simple Ruby library for database abstraction and management.
|
|
87
|
+
test_files: []
|