activequery 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://travis-ci.org/mtunjic/activequery.svg?branch=master)](https://travis-ci.org/mtunjic/activequery)
|
4
4
|
[![Gem Version](https://badge.fury.io/rb/activequery.svg)](https://badge.fury.io/rb/activequery)
|
5
|
+
[![Project Status: WIP – Initial development is in progress, but there has not yet been a stable, usable release suitable for the public.](https://www.repostatus.org/badges/latest/wip.svg)](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
|