activequery 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/Gemfile.lock +5 -1
- data/README.md +30 -6
- data/Rakefile +2 -15
- data/activequery.gemspec +3 -3
- data/lib/active_query.rb +15 -19
- data/lib/active_query/data_adapters/adapter_base.rb +5 -1
- data/lib/active_query/data_adapters/postgresql_adapter.rb +1 -1
- data/lib/active_query/sql_parser.rb +40 -12
- data/lib/active_query/sql_template.rb +4 -9
- data/lib/active_query/version.rb +1 -1
- data/lib/tasks/db.rake +33 -0
- data/lib/tasks/test.rake +31 -0
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 72acf6bff5b58b74b7738ac87e50ca9aa520e4186a3d46b85294fc7671e5bce0
|
4
|
+
data.tar.gz: c6dd9784750fa6d6f8cefbe24bc5333c79b8e3d5a063a27f5f5acdbff89b832d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 86eaede33b0564252eef9216bdee12f9e62a5ee23417281ffa7aff6326fcd521545dcd2b3670ee9c5f38a0df9a4c891aaa8f80aa934eb4f96d46c3821f23f52b
|
7
|
+
data.tar.gz: 3e7423324da0a9733c87ee8b545b4852f1b20988b170fc1fb3a8aecc519b97d951aefccab7c4ed82fc805866cc25b70a92f52882af480f8819bd7de7970813f6
|
data/Gemfile.lock
CHANGED
@@ -1,13 +1,16 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
activequery (0.
|
4
|
+
activequery (0.2.0)
|
5
5
|
strscan (~> 1.0)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
+
benchmark-memory (0.1.2)
|
11
|
+
memory_profiler (~> 0.9)
|
10
12
|
coderay (1.1.2)
|
13
|
+
memory_profiler (0.9.14)
|
11
14
|
method_source (0.9.2)
|
12
15
|
minitest (5.11.3)
|
13
16
|
pg (1.1.4)
|
@@ -22,6 +25,7 @@ PLATFORMS
|
|
22
25
|
|
23
26
|
DEPENDENCIES
|
24
27
|
activequery!
|
28
|
+
benchmark-memory (~> 0.1.2)
|
25
29
|
bundler (~> 1.17)
|
26
30
|
minitest (~> 5.0)
|
27
31
|
pg (~> 1.0)
|
data/README.md
CHANGED
@@ -2,19 +2,28 @@
|
|
2
2
|
|
3
3
|
[](https://travis-ci.org/mtunjic/activequery)
|
4
4
|
[](https://badge.fury.io/rb/activequery)
|
5
|
+
[](https://www.repostatus.org/#wip)
|
5
6
|
|
6
|
-
|
7
|
-
|
7
|
+
|
8
|
+
### SQL & Ruby
|
9
|
+
Ideal for reports, large queries, separating SQL from code logic,
|
10
|
+
query optimization, command and query responsibility segregation (CQRS)
|
8
11
|
|
9
12
|
> Ruby is a great language for writing DSLs, but we don't need a new one. SQL is already a mature DSL.
|
10
13
|
> (Don't agree? Wait until this extra syntax layer breaks down and you start wrestling with a (raw-sql) function.)
|
11
14
|
> ~ Kris Jenkins (yesql)
|
12
15
|
|
13
16
|
```SQL
|
14
|
-
-- name:
|
15
|
-
|
16
|
-
FROM
|
17
|
-
WHERE
|
17
|
+
-- name: find_product
|
18
|
+
-- example with params
|
19
|
+
SELECT * FROM products
|
20
|
+
WHERE units_in_stock > :units
|
21
|
+
AND product_name LIKE ':name';
|
22
|
+
|
23
|
+
-- name: insert_category
|
24
|
+
INSERT INTO categories (category_id, category_name, description)
|
25
|
+
VALUES(<%= rand 248...599 %>, 'books', 'ruby books');
|
26
|
+
|
18
27
|
```
|
19
28
|
|
20
29
|
```ruby
|
@@ -82,6 +91,21 @@ end
|
|
82
91
|
- [ ] Generate CRUD
|
83
92
|
|
84
93
|
|
94
|
+
## Dev Notes:
|
95
|
+
```console
|
96
|
+
# create demo database (sudo su postgres)
|
97
|
+
rake db:create
|
98
|
+
rake db:import
|
99
|
+
rake db:drop
|
100
|
+
|
101
|
+
# tests
|
102
|
+
rake test:unit
|
103
|
+
rake test:integration
|
104
|
+
rake test:system
|
105
|
+
rake test:bench
|
106
|
+
```
|
107
|
+
|
108
|
+
|
85
109
|
## Contributing
|
86
110
|
|
87
111
|
Bug reports and pull requests are welcome on GitHub at https://github.com/mtunjic/activequery.
|
data/Rakefile
CHANGED
@@ -1,19 +1,6 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require "rake/testtask"
|
3
3
|
|
4
|
-
|
5
|
-
t.libs << "test"
|
6
|
-
t.libs << "lib"
|
7
|
-
t.test_files = FileList["test/**/*_test.rb"]
|
8
|
-
end
|
4
|
+
FileList["lib/tasks/*.rake"].each { |f| load f }
|
9
5
|
|
10
|
-
|
11
|
-
desc "Create config file for Rails"
|
12
|
-
task :setup do
|
13
|
-
rails_initializer_path = 'config/initializers/active_query.rb'
|
14
|
-
open(rails_initializer_path, 'w') { |f| f << "This file contains great truths.\n" }
|
15
|
-
puts 'The config file is created to config/initializers.'
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
task :default => :test
|
6
|
+
task :default => "test:unit"
|
data/activequery.gemspec
CHANGED
@@ -41,13 +41,13 @@ Gem::Specification.new do |spec|
|
|
41
41
|
|
42
42
|
|
43
43
|
spec.add_runtime_dependency 'strscan', '~> 1.0'
|
44
|
-
|
44
|
+
#spec.add_runtime_dependency 'zscan', '~> 2.0.5'
|
45
45
|
|
46
46
|
spec.add_development_dependency "bundler", "~> 1.17"
|
47
47
|
spec.add_development_dependency "rake", "~> 10.0"
|
48
48
|
spec.add_development_dependency "minitest", "~> 5.0"
|
49
49
|
spec.add_development_dependency "pry"
|
50
50
|
spec.add_development_dependency 'pg', '~> 1.0'
|
51
|
-
|
52
|
-
|
51
|
+
spec.add_development_dependency 'benchmark-memory', '~> 0.1.2'
|
52
|
+
|
53
53
|
end
|
data/lib/active_query.rb
CHANGED
@@ -32,6 +32,16 @@ module ActiveQuery
|
|
32
32
|
PostgresqlAdapter.open(con_str) { |c| c.exec(query) }
|
33
33
|
end
|
34
34
|
|
35
|
+
def self.run_params(query, params)
|
36
|
+
if query.is_a? Symbol
|
37
|
+
query = self.send(query)
|
38
|
+
end
|
39
|
+
con_str = ActiveQuery.config.connection
|
40
|
+
PostgresqlAdapter.open(con_str) do |conn|
|
41
|
+
conn.exec_params(query, params)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
35
45
|
# async_exec(sql) {|pg_result| block }
|
36
46
|
# This function has the same behavior as sync_exec, but is implemented using the
|
37
47
|
# asynchronous command processing API of libpq.
|
@@ -51,19 +61,6 @@ module ActiveQuery
|
|
51
61
|
end
|
52
62
|
end
|
53
63
|
|
54
|
-
def self.reload!
|
55
|
-
#t = ActiveQuery::SQLTemplate.new(ActiveQuery.config)
|
56
|
-
t = ActiveQuery::SQLTemplate.new(self.config)
|
57
|
-
t.load!
|
58
|
-
t.templates.each do |qname, query|
|
59
|
-
define_singleton_method("#{qname}") do
|
60
|
-
query
|
61
|
-
end
|
62
|
-
end
|
63
|
-
rescue ApplicationError => err
|
64
|
-
$stderr.puts "%p ::SQLTemplate.load! %s" % [ err.class, err.message ]
|
65
|
-
end
|
66
|
-
|
67
64
|
# WIP Init config
|
68
65
|
require "active_query/config"
|
69
66
|
include ActiveQuery::Config
|
@@ -72,9 +69,10 @@ module ActiveQuery
|
|
72
69
|
puts "Loading SQL templates from #{self.config.template_path}"
|
73
70
|
t = ActiveQuery::SQLTemplate.new(self.config)
|
74
71
|
t.load!
|
75
|
-
|
76
|
-
|
77
|
-
|
72
|
+
|
73
|
+
t.templates.each do |query|
|
74
|
+
define_singleton_method(:"#{query.name}") do |params=nil|
|
75
|
+
query.query
|
78
76
|
end
|
79
77
|
end
|
80
78
|
|
@@ -82,7 +80,5 @@ module ActiveQuery
|
|
82
80
|
rescue ApplicationError => err
|
83
81
|
$stderr.puts "%p ::SQLTemplate.load! %s" % [ err.class, err.message ]
|
84
82
|
|
85
|
-
end
|
86
|
-
|
87
|
-
|
88
83
|
|
84
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module ActiveQuery
|
2
2
|
module SQLParser
|
3
|
-
|
3
|
+
|
4
4
|
require 'strscan'
|
5
5
|
class SyntaxError < StandardError; end
|
6
6
|
|
@@ -9,35 +9,63 @@ module ActiveQuery
|
|
9
9
|
WHITESPACE = /\s+/
|
10
10
|
NAME = /^\s*--\s*name\s*:\s*(.+)/
|
11
11
|
END_QUERY = /;/
|
12
|
+
PARAMS = /\(\:\s*([^)]+?)\s*\)/
|
13
|
+
ALL_COMMENTS = /(?-m:--.*)|(?m:--\*.--?\*\/)/
|
14
|
+
PARAM = /:\w+/
|
12
15
|
|
13
|
-
def self.
|
14
|
-
@scanner = StringScanner.new(
|
16
|
+
def self.parse_query(query)
|
17
|
+
@scanner = StringScanner.new(query)
|
15
18
|
@line = 0
|
16
|
-
@
|
19
|
+
@query = Query.new
|
17
20
|
|
18
21
|
until @scanner.eos? || @scanner.check_until(END_QUERY).nil?
|
19
22
|
@line += 1
|
20
|
-
|
23
|
+
parse_line
|
21
24
|
end
|
22
|
-
@
|
25
|
+
@query.query = make_params(@query.query, @query.params)
|
26
|
+
@query
|
23
27
|
end
|
24
28
|
|
25
29
|
private
|
26
30
|
def self.parse_line
|
27
|
-
@
|
31
|
+
@query.params = []
|
28
32
|
|
33
|
+
# Query name match: --name
|
34
|
+
@scanner.skip(NAME)
|
29
35
|
qname = @scanner.captures
|
30
36
|
if qname
|
31
|
-
|
37
|
+
@query.name = qname.first
|
32
38
|
else
|
33
39
|
fail SyntaxError, "error on line #{@line} (pos. #{@scanner.pos})"
|
34
40
|
end
|
35
41
|
|
36
|
-
query = @scanner.
|
37
|
-
unless query
|
38
|
-
|
42
|
+
@query.query = @scanner.rest
|
43
|
+
fail SyntaxError unless @query.query
|
44
|
+
|
45
|
+
# All params
|
46
|
+
until @scanner.check_until(PARAM).nil?
|
47
|
+
@scanner.scan_until(PARAM)
|
48
|
+
@query.params.push @scanner.matched
|
49
|
+
end
|
50
|
+
|
51
|
+
# End of query match: ;
|
52
|
+
@scanner.scan_until(END_QUERY)
|
53
|
+
end
|
54
|
+
|
55
|
+
# and this only work for postgres - split transform from struct
|
56
|
+
def self.make_params(query, params)
|
57
|
+
if params && query
|
58
|
+
params.each_with_index do |param, i|
|
59
|
+
pos = "$#{i+1}"
|
60
|
+
query.gsub!(param, pos)
|
61
|
+
end
|
62
|
+
query.strip if query
|
63
|
+
else
|
64
|
+
query.strip if query
|
39
65
|
end
|
40
|
-
Query.new(qname, "", "", query)
|
41
66
|
end
|
67
|
+
|
68
|
+
|
42
69
|
end
|
43
70
|
end
|
71
|
+
|
@@ -15,7 +15,7 @@ module ActiveQuery
|
|
15
15
|
|
16
16
|
def initialize(config = Configuration.new)
|
17
17
|
@config = config
|
18
|
-
@templates =
|
18
|
+
@templates = []
|
19
19
|
end
|
20
20
|
|
21
21
|
def load!
|
@@ -25,18 +25,14 @@ module ActiveQuery
|
|
25
25
|
else
|
26
26
|
Find.find(template_path) do |path|
|
27
27
|
if File.basename(path) =~ /sql$/
|
28
|
-
parse_sql(path)
|
28
|
+
@templates << parse_sql(path)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
34
|
def templates
|
35
|
-
@templates ||=
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.get(query)
|
39
|
-
@templates[query.to_s]
|
35
|
+
@templates ||= []
|
40
36
|
end
|
41
37
|
|
42
38
|
private
|
@@ -46,8 +42,7 @@ module ActiveQuery
|
|
46
42
|
|
47
43
|
def parse_sql(path)
|
48
44
|
sql_template = read_sql_file(path: path)
|
49
|
-
|
50
|
-
sql.each {|q| @templates[q.name] = q.query.strip }
|
45
|
+
ActiveQuery::SQLParser.parse_query(sql_template)
|
51
46
|
end
|
52
47
|
|
53
48
|
end
|
data/lib/active_query/version.rb
CHANGED
data/lib/tasks/db.rake
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
namespace :db do
|
2
|
+
desc "Create test database "
|
3
|
+
task :create do
|
4
|
+
# or createuser --interactive joe
|
5
|
+
sh "sudo su postgres"
|
6
|
+
sh "dropdb --if-exists northwind"
|
7
|
+
sh "dropuser --if-exists northwind_user"
|
8
|
+
sh "createdb northwind"
|
9
|
+
puts 'The demo db is created.'
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Import data "
|
13
|
+
task :import do
|
14
|
+
cmds = %q{
|
15
|
+
psql northwind < ./test/integration/northwind/northwind.sql
|
16
|
+
|
17
|
+
psql --no-psqlrc template1 -c "create user northwind_user;"
|
18
|
+
psql --no-psqlrc template1 -c "alter user northwind_user password 'thewindisblowing';"
|
19
|
+
psql --no-psqlrc template1 -c "grant all on DATABASE northwind to northwind_user;"
|
20
|
+
psql --no-psqlrc northwind -c "GRANT ALL on ALL tables IN SCHEMA public to northwind_user"
|
21
|
+
}
|
22
|
+
sh cmds
|
23
|
+
puts 'The northwind db is imported.'
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "Drop test database "
|
27
|
+
task :drop do
|
28
|
+
sh "dropdb --if-exists northwind"
|
29
|
+
sh "dropuser --if-exists northwind_user"
|
30
|
+
puts 'The demo db is droped.'
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/lib/tasks/test.rake
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
namespace :test do
|
2
|
+
|
3
|
+
desc "Run unit tests"
|
4
|
+
Rake::TestTask.new(:unit) do |t|
|
5
|
+
t.libs << "test"
|
6
|
+
t.libs << "lib"
|
7
|
+
t.test_files = FileList["test/*_test.rb"]
|
8
|
+
end
|
9
|
+
|
10
|
+
desc "Run system tests"
|
11
|
+
Rake::TestTask.new(:system) do |t|
|
12
|
+
t.libs << "test"
|
13
|
+
t.libs << "lib"
|
14
|
+
t.test_files = FileList["test/system/*_test.rb"]
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Run integration tests"
|
18
|
+
Rake::TestTask.new(:integration) do |t|
|
19
|
+
t.libs << "test"
|
20
|
+
t.libs << "lib"
|
21
|
+
t.test_files = FileList["test/integration/*_test.rb"]
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "Run benchmark"
|
25
|
+
Rake::TestTask.new(:bench) do |t|
|
26
|
+
t.libs << "test"
|
27
|
+
t.libs << "lib"
|
28
|
+
t.test_files = FileList["test/benchmark/*_bench.rb"]
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activequery
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marko
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-08-
|
11
|
+
date: 2019-08-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: strscan
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '1.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: benchmark-memory
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.1.2
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.1.2
|
97
111
|
description: Raw sql with ruby - ideal for reports, large queries, separating SQL
|
98
112
|
from code logic and CQRS
|
99
113
|
email:
|
@@ -123,6 +137,8 @@ files:
|
|
123
137
|
- lib/active_query/sql_renderer.rb
|
124
138
|
- lib/active_query/sql_template.rb
|
125
139
|
- lib/active_query/version.rb
|
140
|
+
- lib/tasks/db.rake
|
141
|
+
- lib/tasks/test.rake
|
126
142
|
homepage: https://github.com/mtunjic/activequery
|
127
143
|
licenses:
|
128
144
|
- MIT
|