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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 604df6bd427cfff0873ed44e5812adf4fc61584a6c51dbba5ec92fb18f5cac11
4
- data.tar.gz: 3b99b1b2cc2b17bbaefcda6e89666dffa5ef9c99b68b6484012318e60cf6a6cb
3
+ metadata.gz: 72acf6bff5b58b74b7738ac87e50ca9aa520e4186a3d46b85294fc7671e5bce0
4
+ data.tar.gz: c6dd9784750fa6d6f8cefbe24bc5333c79b8e3d5a063a27f5f5acdbff89b832d
5
5
  SHA512:
6
- metadata.gz: ce0c3882bd16c514e661ce1942f2f028754e2a1995766a71da3080abfd50ce465619822b853a56b7e7186529cc90870590b70e1b5dc4d54dadc76f915461e958
7
- data.tar.gz: 35321a4999149a389919979d7f8363a786bd6f94a7d0fe825b8d36a70ecb8aff3394207fab5969967ee17c2e5ad41e335c937efaf78bd62f4c89015b76647836
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.1.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
- ### SQL & ruby
7
- Ideal for reports, large queries, separating SQL from code logic, command and query responsibility segregation (CQRS)
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: users_by_country
15
- SELECT *
16
- FROM users
17
- WHERE country_code = :country_code
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
- Rake::TestTask.new(:test) do |t|
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
- namespace :active_query do
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
- t.templates.each do |qname, query|
76
- define_singleton_method("#{qname}") do
77
- query
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
@@ -7,9 +7,13 @@ module ActiveQuery
7
7
  @connection = connection
8
8
  end
9
9
 
10
- def exec(param)
10
+ def exec(conn)
11
11
  raise "Not implemented"
12
12
  end
13
+
14
+ def exec_params(params)
15
+ raise "Not implemented"
16
+ end
13
17
  end
14
18
  end
15
19
  end
@@ -11,7 +11,7 @@ module ActiveQuery
11
11
  yield(conn)
12
12
  rescue PG::Error => err
13
13
  $stderr.puts "%p PgConn::open: %s" % [ err.class, err.message ]
14
- conn.reset
14
+ conn.reset if conn
15
15
  ensure
16
16
  conn.close if conn
17
17
  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.parse_file(text)
14
- @scanner = StringScanner.new(text)
16
+ def self.parse_query(query)
17
+ @scanner = StringScanner.new(query)
15
18
  @line = 0
16
- @queries = []
19
+ @query = Query.new
17
20
 
18
21
  until @scanner.eos? || @scanner.check_until(END_QUERY).nil?
19
22
  @line += 1
20
- @queries << parse_line
23
+ parse_line
21
24
  end
22
- @queries
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
- @scanner.skip(NAME)
31
+ @query.params = []
28
32
 
33
+ # Query name match: --name
34
+ @scanner.skip(NAME)
29
35
  qname = @scanner.captures
30
36
  if qname
31
- qname = qname.first
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.scan_until(END_QUERY)
37
- unless query
38
- fail SyntaxError, "error on line #{@line} (pos. #{@scanner.pos})"
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 ||= Hash.new
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
- sql = ActiveQuery::SQLParser.parse_file(sql_template)
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
@@ -1,3 +1,3 @@
1
1
  module ActiveQuery
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
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
@@ -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.1.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-05 00:00:00.000000000 Z
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