qdsl 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +120 -0
- data/Rakefile +10 -0
- data/lib/and.rb +16 -0
- data/lib/boolean.rb +14 -0
- data/lib/column.rb +43 -0
- data/lib/context.rb +23 -0
- data/lib/equals.rb +23 -0
- data/lib/expression.rb +21 -0
- data/lib/funky_proxy.rb +20 -0
- data/lib/inner_join.rb +11 -0
- data/lib/inner_join_builder.rb +14 -0
- data/lib/or.rb +16 -0
- data/lib/qdsl.rb +27 -0
- data/lib/qdsl/version.rb +4 -0
- data/lib/select.rb +95 -0
- data/lib/table.rb +15 -0
- data/lib/table_query.rb +15 -0
- data/qdsl.gemspec +25 -0
- data/test/column_test.rb +115 -0
- data/test/context_test.rb +19 -0
- data/test/end_to_end_test.rb +26 -0
- data/test/expression_test.rb +108 -0
- data/test/inner_join_test.rb +65 -0
- data/test/minitest_helper.rb +6 -0
- data/test/query_test.rb +40 -0
- data/test/select_test.rb +96 -0
- data/test/subquery_test.rb +66 -0
- data/test/where_test.rb +21 -0
- metadata +128 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cd1c1b61ff92582af4dbb84566ac95128698db36
|
4
|
+
data.tar.gz: 746092cbe04dfa4b583dc7f3a0e2e9b43976ee1a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 187f3afba4a996b5f2010c4b67b0645daa9d4912f837d3d7ce28b7ee34fe59fe545b3791403b3bb5098037f2fbb2e085d2beda687405162d65874ab6ce302a8c
|
7
|
+
data.tar.gz: 7935cb02ccdecf88385d27d68aca1c2c9faa10823aadbe5032332ec3a4b233485c0eb74e8c13ff5c77044da678bab626c399a1f301c5fe44a2a1510861a8beda
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Richard Cook
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# qdsl
|
2
|
+
|
3
|
+
[![Install gem](https://badge.fury.io/rb/qdsl.png)](https://rubygems.org/gems/qdsl)
|
4
|
+
[![Build status](https://travis-ci.org/rcook/qdsl.png)](https://travis-ci.org/rcook/qdsl)
|
5
|
+
|
6
|
+
qdsl is SQL query builder DSL for Ruby: think of it as "hygienic macros for
|
7
|
+
SQL". Note that this gem is in its very early development stages and is very far
|
8
|
+
from complete. Here are the main talking points:
|
9
|
+
|
10
|
+
* It's most likely riddled with bugs.
|
11
|
+
* It is intended to have minimal external dependencies: it doesn't currently
|
12
|
+
depend on any database adapters or anything like that since it is intended to
|
13
|
+
generate only text and query parameter bindings.
|
14
|
+
* It will generate PostgreSQL-compatible SQL, since that's what I care about.
|
15
|
+
* It is intended to allow clean and straightforward composition of SQL queries
|
16
|
+
and subqueries.
|
17
|
+
* It takes a similar approach to other SQL Ruby-embedded DSLs (e.g.
|
18
|
+
[SQLDSL](https://github.com/dydx/SQLDSL)) but differs as follows:
|
19
|
+
* It attempts to validate the query: column names are currently supported and
|
20
|
+
the intention is to support column types too.
|
21
|
+
* It automatically generates table aliases in order to cleanly scope column
|
22
|
+
names and to remove some of the confusion around joining tables with identically
|
23
|
+
named columns.
|
24
|
+
|
25
|
+
## Installation
|
26
|
+
|
27
|
+
Add this line to your application's Gemfile:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
gem 'qdsl'
|
31
|
+
```
|
32
|
+
|
33
|
+
And then execute:
|
34
|
+
|
35
|
+
```bash
|
36
|
+
$ bundle
|
37
|
+
```
|
38
|
+
|
39
|
+
Or install it yourself as:
|
40
|
+
|
41
|
+
```bash
|
42
|
+
$ gem install qdsl
|
43
|
+
```
|
44
|
+
|
45
|
+
## Usage
|
46
|
+
|
47
|
+
qdsl allows us to build SQL queries in a fairly natural way as a series of
|
48
|
+
Ruby chained method calls. A few examples are given below.
|
49
|
+
|
50
|
+
### Reference the gem
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
require 'qdsl'
|
54
|
+
```
|
55
|
+
|
56
|
+
### Define a table
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
clients = Qdsl::Table.new(
|
60
|
+
:clients,
|
61
|
+
:id,
|
62
|
+
:first_name,
|
63
|
+
:last_name
|
64
|
+
)
|
65
|
+
```
|
66
|
+
|
67
|
+
This does not connect to a database or anything like that. This is just a way
|
68
|
+
to define a table name and the list of column names from which we can build
|
69
|
+
subsequent queries. Eventually, we might support generating these `Table`
|
70
|
+
objects from Active Record models, but until that day comes, this is how we
|
71
|
+
define data sources.
|
72
|
+
|
73
|
+
### Define a query
|
74
|
+
|
75
|
+
This is based on the most complex query defined in the gem's test suite:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
subquery = Qdsl::select(:id, :first_name, :last_name).
|
79
|
+
from(clients)
|
80
|
+
query = Qdsl::select { |t0, t1| [t0.first_name, t1.last_name] }.
|
81
|
+
from(subquery).
|
82
|
+
inner_join(subquery).on { |t0, t1| t0.id.equals(t1.id) }.
|
83
|
+
where { |t0, t1| t0.first_name.equals('str0').and(t1.last_name.equals('str1')) }
|
84
|
+
```
|
85
|
+
|
86
|
+
Calling `render` on the query object `query` yields SQL that looks something
|
87
|
+
like the following:
|
88
|
+
|
89
|
+
```sql
|
90
|
+
SELECT _00.first_name, _01.last_name
|
91
|
+
FROM (
|
92
|
+
SELECT _02.id, _02.first_name, _02.last_name
|
93
|
+
FROM clients AS _02
|
94
|
+
) AS _00
|
95
|
+
INNER JOIN (
|
96
|
+
SELECT _03.id, _03.first_name, _03.last_name
|
97
|
+
FROM clients AS _03
|
98
|
+
) AS _01
|
99
|
+
ON _00.id = _01.id
|
100
|
+
WHERE (_00.first_name = :_param00) AND (_01.last_name = :_param01)
|
101
|
+
```
|
102
|
+
|
103
|
+
The `parameters` attribute on the `Context` object populated by `render` will
|
104
|
+
define the keys `_param00` and `_param01` with values `str0` and `str1`
|
105
|
+
respectively. Notice the following:
|
106
|
+
|
107
|
+
* The use of code blocks to perform validation of column names
|
108
|
+
* Rudimentary support for boolean expressions
|
109
|
+
* Replacement of string literals with query parameters
|
110
|
+
* Automatic generation of table aliases in order to avoid column name collisions
|
111
|
+
|
112
|
+
|
113
|
+
## Contributing
|
114
|
+
|
115
|
+
1. Fork it from [here](https://github.com/rcook/qdsl/fork)
|
116
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
117
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
118
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
119
|
+
5. Create a new pull request
|
120
|
+
|
data/Rakefile
ADDED
data/lib/and.rb
ADDED
data/lib/boolean.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module Qdsl
|
2
|
+
class Boolean < Expression
|
3
|
+
def initialize(operator, exprs)
|
4
|
+
@operator = operator
|
5
|
+
@exprs = exprs
|
6
|
+
end
|
7
|
+
|
8
|
+
def render(context, ids)
|
9
|
+
expr_results = @exprs.collect { |x| render_operand(context, ids, x) }
|
10
|
+
expr_results.collect { |x| "(#{x})" }.join(" #{@operator} ")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
data/lib/column.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module Qdsl
|
2
|
+
class Column
|
3
|
+
attr_reader :name, :alias_name, :source
|
4
|
+
|
5
|
+
def self.[](obj)
|
6
|
+
if obj.is_a?(Column)
|
7
|
+
obj
|
8
|
+
elsif obj.is_a?(String) || obj.is_a?(Symbol)
|
9
|
+
Column.new(obj)
|
10
|
+
else
|
11
|
+
raise "Cannot convert object of class \"#{obj.class.name}\" to column"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(name, alias_name = nil, source = nil)
|
16
|
+
@name = name.to_s
|
17
|
+
@alias_name = alias_name.nil? ? nil : alias_name.to_s
|
18
|
+
@source = source
|
19
|
+
end
|
20
|
+
|
21
|
+
def with_name(name)
|
22
|
+
Column.new(name, @alias_name, @source)
|
23
|
+
end
|
24
|
+
|
25
|
+
def with_alias_name(alias_name)
|
26
|
+
Column.new(@name, alias_name, @source)
|
27
|
+
end
|
28
|
+
alias_method :as, :with_alias_name
|
29
|
+
|
30
|
+
def with_source(source)
|
31
|
+
Column.new(@name, @alias_name, source)
|
32
|
+
end
|
33
|
+
|
34
|
+
def render(context, id)
|
35
|
+
@alias_name ? "#{id}.#{@name} AS #{@alias_name}" : "#{id}.#{@name}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def equals(column)
|
39
|
+
Equals.new(self, column)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
data/lib/context.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Qdsl
|
2
|
+
class Context
|
3
|
+
attr_reader :parameters
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@id_base = 0
|
7
|
+
@parameters = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def id
|
11
|
+
result = "_#{@id_base.to_s.rjust(2, '0')}"
|
12
|
+
@id_base += 1
|
13
|
+
result
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_parameter(value)
|
17
|
+
parameter_id = "_param#{@parameters.size.to_s.rjust(2, '0')}"
|
18
|
+
@parameters[parameter_id] = value
|
19
|
+
parameter_id
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
data/lib/equals.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Qdsl
|
2
|
+
class Equals < Expression
|
3
|
+
def initialize(column0, column1)
|
4
|
+
@column0 = column0
|
5
|
+
@column1 = column1
|
6
|
+
end
|
7
|
+
|
8
|
+
def and(expr)
|
9
|
+
And.new([self, expr])
|
10
|
+
end
|
11
|
+
|
12
|
+
def or(expr)
|
13
|
+
Or.new([self, expr])
|
14
|
+
end
|
15
|
+
|
16
|
+
def render(context, ids)
|
17
|
+
column0_result = render_operand(context, ids, @column0)
|
18
|
+
column1_result = render_operand(context, ids, @column1)
|
19
|
+
"#{column0_result} = #{column1_result}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
data/lib/expression.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Qdsl
|
2
|
+
class Expression
|
3
|
+
protected
|
4
|
+
|
5
|
+
def render_operand(context, ids, operand)
|
6
|
+
if operand.is_a?(String)
|
7
|
+
parameter_id = context.add_parameter(operand)
|
8
|
+
":#{parameter_id}"
|
9
|
+
elsif operand.is_a?(TrueClass)
|
10
|
+
'TRUE'
|
11
|
+
elsif operand.is_a?(FalseClass)
|
12
|
+
'FALSE'
|
13
|
+
elsif operand.is_a?(Expression)
|
14
|
+
operand.render(context, ids)
|
15
|
+
else
|
16
|
+
operand.render(context, ids[operand.source])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
data/lib/funky_proxy.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Qdsl
|
2
|
+
class FunkyProxy
|
3
|
+
def initialize(source)
|
4
|
+
@source = source
|
5
|
+
source.column_names.each do |column|
|
6
|
+
(class << self; self; end).class_eval do
|
7
|
+
define_method column do
|
8
|
+
Column.new(column, nil, source)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](name)
|
15
|
+
raise unless @source.column_names.include?(name.to_s)
|
16
|
+
Column.new(name.to_s, nil, @source)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
data/lib/inner_join.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module Qdsl
|
2
|
+
class InnerJoinBuilder
|
3
|
+
def initialize(query_builder, source)
|
4
|
+
@query_builder = query_builder
|
5
|
+
@source = source
|
6
|
+
end
|
7
|
+
|
8
|
+
def on
|
9
|
+
predicate = yield(FunkyProxy.new(@query_builder.source), FunkyProxy.new(@source))
|
10
|
+
@query_builder.add_inner_join(@source, predicate)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
data/lib/or.rb
ADDED
data/lib/qdsl.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'qdsl/version'
|
2
|
+
|
3
|
+
%w{
|
4
|
+
expression
|
5
|
+
boolean
|
6
|
+
and
|
7
|
+
column
|
8
|
+
context
|
9
|
+
equals
|
10
|
+
funky_proxy
|
11
|
+
inner_join
|
12
|
+
inner_join_builder
|
13
|
+
or
|
14
|
+
qdsl
|
15
|
+
select
|
16
|
+
table
|
17
|
+
table_query
|
18
|
+
}.each do |file_name|
|
19
|
+
require_relative file_name
|
20
|
+
end
|
21
|
+
|
22
|
+
module Qdsl
|
23
|
+
def self.select(*columns, &block)
|
24
|
+
Select.new(columns.collect { |x| Column[x] }, &block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
data/lib/qdsl/version.rb
ADDED
data/lib/select.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
module Qdsl
|
2
|
+
class SelectQuery
|
3
|
+
attr_reader :column_names
|
4
|
+
|
5
|
+
def initialize(select, column_names)
|
6
|
+
@select = select
|
7
|
+
@column_names = column_names
|
8
|
+
end
|
9
|
+
|
10
|
+
def render(context, depth, id)
|
11
|
+
select_result = @select.render(context, depth)
|
12
|
+
"(\n#{select_result}) AS #{id}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Select
|
17
|
+
attr_reader :source
|
18
|
+
|
19
|
+
def initialize(columns, &block)
|
20
|
+
@columns = columns
|
21
|
+
@block = block
|
22
|
+
@inner_joins = []
|
23
|
+
end
|
24
|
+
|
25
|
+
def from(source)
|
26
|
+
@source = source.create_query
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_query
|
31
|
+
# Clone columns?
|
32
|
+
column_names = @columns.collect(&:name)
|
33
|
+
SelectQuery.new(self, column_names)
|
34
|
+
end
|
35
|
+
|
36
|
+
def where
|
37
|
+
sources = [@source] + @inner_joins.collect(&:source)
|
38
|
+
proxies = sources.collect { |x| FunkyProxy.new(x) }
|
39
|
+
@where = yield(proxies.size == 1 ? proxies.first : proxies)
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def inner_join(source)
|
44
|
+
InnerJoinBuilder.new(self, source.create_query)
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_inner_join(source, predicate)
|
48
|
+
@inner_joins << InnerJoin.new(source, predicate)
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
def render(context = nil, depth = 0)
|
53
|
+
context ||= Context.new
|
54
|
+
|
55
|
+
indent = ' ' * depth
|
56
|
+
|
57
|
+
unknown_columns = @columns.select { |x| !@source.column_names.include?(x.name) }
|
58
|
+
raise "One or more unknown columns: #{unknown_columns.collect(&:name).join(', ')}" unless unknown_columns.empty?
|
59
|
+
|
60
|
+
columns = @columns.collect { |x| x.with_source(@source) }
|
61
|
+
|
62
|
+
sources = [@source] + @inner_joins.collect(&:source)
|
63
|
+
|
64
|
+
extra_columns = if @block
|
65
|
+
proxies = sources.collect { |x| FunkyProxy.new(x) }
|
66
|
+
[*@block.call(proxies.size == 1 ? proxies.first : proxies)]
|
67
|
+
else
|
68
|
+
[]
|
69
|
+
end
|
70
|
+
|
71
|
+
ids = sources.inject({}) { |acc, x| acc[x] = context.id; acc }
|
72
|
+
|
73
|
+
column_results = (columns + extra_columns).collect { |x| x.render(context, ids[x.source]) }
|
74
|
+
|
75
|
+
fragments = []
|
76
|
+
|
77
|
+
fragments << "#{indent}SELECT #{column_results.join(', ')}\n"
|
78
|
+
fragments << "#{indent}FROM #{@source.render(context, depth + 1, ids[@source])}\n"
|
79
|
+
|
80
|
+
@inner_joins.each do |inner_join|
|
81
|
+
predicate_result = inner_join.predicate.render(context, ids)
|
82
|
+
fragments << "#{indent}INNER JOIN #{inner_join.source.render(context, depth + 1, ids[inner_join.source])}\n"
|
83
|
+
fragments << "#{indent}ON #{predicate_result}\n"
|
84
|
+
end
|
85
|
+
|
86
|
+
if @where
|
87
|
+
predicate_result = @where.render(context, ids)
|
88
|
+
fragments << "#{indent}WHERE #{predicate_result}"
|
89
|
+
end
|
90
|
+
|
91
|
+
fragments.join
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
data/lib/table.rb
ADDED
data/lib/table_query.rb
ADDED
data/qdsl.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'qdsl/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'qdsl'
|
8
|
+
spec.version = Qdsl::VERSION
|
9
|
+
spec.authors = ['Richard Cook']
|
10
|
+
spec.email = ['rcook@rcook.org']
|
11
|
+
spec.summary = %q{SQL query builder DSL for Ruby}
|
12
|
+
spec.description = %q{Think of it as "hygienic macros for SQL}
|
13
|
+
spec.homepage = ''
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
22
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
23
|
+
spec.add_development_dependency 'minitest'
|
24
|
+
end
|
25
|
+
|
data/test/column_test.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require_relative 'minitest_helper'
|
2
|
+
|
3
|
+
module Qdsl
|
4
|
+
class ColumnTest < Minitest::Test
|
5
|
+
def test_convert_from_column
|
6
|
+
column = Column.new('name', 'alias_name', 'source')
|
7
|
+
result = Column[column]
|
8
|
+
assert_equal column, result
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_convert_from_string
|
12
|
+
result = Column['name']
|
13
|
+
assert_equal 'name', result.name
|
14
|
+
assert_nil result.alias_name
|
15
|
+
assert_nil result.source
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_convert_from_symbol
|
19
|
+
result = Column[:name]
|
20
|
+
assert_equal 'name', result.name
|
21
|
+
assert_nil result.alias_name
|
22
|
+
assert_nil result.source
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_initialize_strings
|
26
|
+
result = Column.new('name', 'alias_name', 'source')
|
27
|
+
assert_equal 'name', result.name
|
28
|
+
assert_equal 'alias_name', result.alias_name
|
29
|
+
assert_equal 'source', result.source
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_initialize_symbols
|
33
|
+
result = Column.new(:name, :alias_name, :source)
|
34
|
+
assert_equal 'name', result.name
|
35
|
+
assert_equal 'alias_name', result.alias_name
|
36
|
+
assert_equal :source, result.source
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_initialize_name_only_string
|
40
|
+
result = Column.new('name')
|
41
|
+
assert_equal 'name', result.name
|
42
|
+
assert_nil result.alias_name
|
43
|
+
assert_nil result.source
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_initialize_name_only_symbol
|
47
|
+
result = Column.new(:name)
|
48
|
+
assert_equal 'name', result.name
|
49
|
+
assert_nil result.alias_name
|
50
|
+
assert_nil result.source
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_with_name_string
|
54
|
+
result = Column.new('name', 'alias_name', 'source').with_name('new_name')
|
55
|
+
assert_equal 'new_name', result.name
|
56
|
+
assert_equal 'alias_name', result.alias_name
|
57
|
+
assert_equal 'source', result.source
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_with_name_symbol
|
61
|
+
result = Column.new('name', 'alias_name', 'source').with_name(:new_name)
|
62
|
+
assert_equal 'new_name', result.name
|
63
|
+
assert_equal 'alias_name', result.alias_name
|
64
|
+
assert_equal 'source', result.source
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_with_alias_name_string
|
68
|
+
result = Column.new('name', 'alias_name', 'source').with_alias_name('new_alias_name')
|
69
|
+
assert_equal 'name', result.name
|
70
|
+
assert_equal 'new_alias_name', result.alias_name
|
71
|
+
assert_equal 'source', result.source
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_with_alias_name_symbol
|
75
|
+
result = Column.new('name', 'alias_name', 'source').with_alias_name(:new_alias_name)
|
76
|
+
assert_equal 'name', result.name
|
77
|
+
assert_equal 'new_alias_name', result.alias_name
|
78
|
+
assert_equal 'source', result.source
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_with_source
|
82
|
+
result = Column.new('name', 'alias_name', 'source').with_source('new_source')
|
83
|
+
assert_equal 'name', result.name
|
84
|
+
assert_equal 'alias_name', result.alias_name
|
85
|
+
assert_equal 'new_source', result.source
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_as_string
|
89
|
+
result = Column.new('name', 'alias_name', 'source').as('new_name_alias')
|
90
|
+
assert_equal 'name', result.name
|
91
|
+
assert_equal 'new_name_alias', result.alias_name
|
92
|
+
assert_equal 'source', result.source
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_as_symbol
|
96
|
+
result = Column.new('name', 'alias_name', 'source').as(:new_name_alias)
|
97
|
+
assert_equal 'name', result.name
|
98
|
+
assert_equal 'new_name_alias', result.alias_name
|
99
|
+
assert_equal 'source', result.source
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_render_name_only
|
103
|
+
column = Column.new('name')
|
104
|
+
result = column.render(Context.new, 'id')
|
105
|
+
assert_equal 'id.name', result
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_render_alias_name
|
109
|
+
column = Column.new('name', 'alias_name')
|
110
|
+
result = column.render(Context.new, 'id')
|
111
|
+
assert_equal 'id.name AS alias_name', result
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative 'minitest_helper'
|
2
|
+
require_relative 'query_test'
|
3
|
+
|
4
|
+
module Qdsl
|
5
|
+
class ContextTest < QueryTest
|
6
|
+
def test_simple
|
7
|
+
query = Qdsl::select(:first_name).from(@clients).where { |t| t.id.equals('Cook') }
|
8
|
+
context = Context.new
|
9
|
+
result = query.render(context)
|
10
|
+
|
11
|
+
assert_sql 'SELECT _00.first_name ' +
|
12
|
+
'FROM clients AS _00 ' +
|
13
|
+
'WHERE _00.id = :_param00', result
|
14
|
+
assert_equal 1, context.parameters.size
|
15
|
+
assert_equal 'Cook', context.parameters['_param00']
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative 'minitest_helper'
|
2
|
+
require_relative 'query_test'
|
3
|
+
|
4
|
+
module Qdsl
|
5
|
+
class EndToEndTest < QueryTest
|
6
|
+
def test_aliases
|
7
|
+
query = Qdsl::select(
|
8
|
+
Column[:id].as(:alias0),
|
9
|
+
Column['first_name'].as(:alias1),
|
10
|
+
Column.new(:last_name).as(:alias2)
|
11
|
+
).from(@clients)
|
12
|
+
result = query.render
|
13
|
+
|
14
|
+
assert_sql 'SELECT _00.id AS alias0, _00.first_name AS alias1, _00.last_name AS alias2 FROM clients AS _00', result
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_unknown_column
|
18
|
+
query = Qdsl::select(:unknown).from(@clients)
|
19
|
+
|
20
|
+
assert_raises RuntimeError do
|
21
|
+
query.render
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require_relative 'minitest_helper'
|
2
|
+
|
3
|
+
module Qdsl
|
4
|
+
class ExpressionTest < QueryTest
|
5
|
+
def test_simple_and
|
6
|
+
query = Qdsl::select(:id).
|
7
|
+
from(@clients).
|
8
|
+
where { |t| t.first_name.equals('Richard').and(t.last_name.equals('Cook')) }
|
9
|
+
|
10
|
+
context = Context.new
|
11
|
+
result = query.render(context)
|
12
|
+
|
13
|
+
assert_sql 'SELECT _00.id ' +
|
14
|
+
'FROM clients AS _00 ' +
|
15
|
+
'WHERE (_00.first_name = :_param00) AND (_00.last_name = :_param01)', result
|
16
|
+
assert_equal 2, context.parameters.size
|
17
|
+
assert_equal 'Richard', context.parameters['_param00']
|
18
|
+
assert_equal 'Cook', context.parameters['_param01']
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_multiple_and
|
22
|
+
query = Qdsl::select(:id).
|
23
|
+
from(@clients).
|
24
|
+
where { |t| t.first_name.equals('Richard').and(t.last_name.equals('Cook')).and(t.id.equals('ID')) }
|
25
|
+
|
26
|
+
context = Context.new
|
27
|
+
result = query.render(context)
|
28
|
+
|
29
|
+
assert_sql 'SELECT _00.id ' +
|
30
|
+
'FROM clients AS _00 ' +
|
31
|
+
'WHERE (_00.first_name = :_param00) AND (_00.last_name = :_param01) AND (_00.id = :_param02)', result
|
32
|
+
assert_equal 3, context.parameters.size
|
33
|
+
assert_equal 'Richard', context.parameters['_param00']
|
34
|
+
assert_equal 'Cook', context.parameters['_param01']
|
35
|
+
assert_equal 'ID', context.parameters['_param02']
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_simple_or
|
39
|
+
query = Qdsl::select(:id).
|
40
|
+
from(@clients).
|
41
|
+
where { |t| t.first_name.equals('Richard').or(t.last_name.equals('Cook')) }
|
42
|
+
|
43
|
+
context = Context.new
|
44
|
+
result = query.render(context)
|
45
|
+
|
46
|
+
assert_sql 'SELECT _00.id ' +
|
47
|
+
'FROM clients AS _00 ' +
|
48
|
+
'WHERE (_00.first_name = :_param00) OR (_00.last_name = :_param01)', result
|
49
|
+
assert_equal 2, context.parameters.size
|
50
|
+
assert_equal 'Richard', context.parameters['_param00']
|
51
|
+
assert_equal 'Cook', context.parameters['_param01']
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_multiple_or
|
55
|
+
query = Qdsl::select(:id).
|
56
|
+
from(@clients).
|
57
|
+
where { |t| t.first_name.equals('Richard').or(t.last_name.equals('Cook')).or(t.id.equals('ID')) }
|
58
|
+
|
59
|
+
context = Context.new
|
60
|
+
result = query.render(context)
|
61
|
+
|
62
|
+
assert_sql 'SELECT _00.id ' +
|
63
|
+
'FROM clients AS _00 ' +
|
64
|
+
'WHERE (_00.first_name = :_param00) OR (_00.last_name = :_param01) OR (_00.id = :_param02)', result
|
65
|
+
assert_equal 3, context.parameters.size
|
66
|
+
assert_equal 'Richard', context.parameters['_param00']
|
67
|
+
assert_equal 'Cook', context.parameters['_param01']
|
68
|
+
assert_equal 'ID', context.parameters['_param02']
|
69
|
+
end
|
70
|
+
|
71
|
+
def big_or(ids, t)
|
72
|
+
ids.collect { |x| t[x].equals(true) }.inject { |acc, x| acc.or(x) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_multiple_or2
|
76
|
+
query = Qdsl::select(:id).
|
77
|
+
from(@clients).
|
78
|
+
where { |t| big_or([:value0, :value1, :value2, :value3], t) }
|
79
|
+
result = query.render
|
80
|
+
|
81
|
+
assert_sql 'SELECT _00.id ' +
|
82
|
+
'FROM clients AS _00 ' +
|
83
|
+
'WHERE (_00.value0 = TRUE) OR (_00.value1 = TRUE) OR (_00.value2 = TRUE) OR (_00.value3 = TRUE)', result
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_boolean
|
87
|
+
query = Qdsl::select(:id).
|
88
|
+
from(@clients).
|
89
|
+
where { |t| t.first_name.equals('Richard').or(t.last_name.equals('Cook')).and(t.id.equals('ID').or(t.id.equals('OTHER_ID'))) }
|
90
|
+
|
91
|
+
context = Context.new
|
92
|
+
result = query.render(context)
|
93
|
+
|
94
|
+
assert_sql 'SELECT _00.id ' +
|
95
|
+
'FROM clients AS _00 ' +
|
96
|
+
'WHERE ((_00.first_name = :_param00) OR ' +
|
97
|
+
'(_00.last_name = :_param01)) ' +
|
98
|
+
'AND ((_00.id = :_param02) OR ' +
|
99
|
+
'(_00.id = :_param03))', result
|
100
|
+
assert_equal 4, context.parameters.size
|
101
|
+
assert_equal 'Richard', context.parameters['_param00']
|
102
|
+
assert_equal 'Cook', context.parameters['_param01']
|
103
|
+
assert_equal 'ID', context.parameters['_param02']
|
104
|
+
assert_equal 'OTHER_ID', context.parameters['_param03']
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require_relative 'minitest_helper'
|
2
|
+
|
3
|
+
module Qdsl
|
4
|
+
class InnerJoinTest < QueryTest
|
5
|
+
def test_inner_join_simple
|
6
|
+
query = Qdsl::select { |t0, t1| [t0.id, t1.member_id] }.
|
7
|
+
from(@clients).
|
8
|
+
inner_join(@members).on { |f, j| f.id.equals(j.id) }
|
9
|
+
result = query.render
|
10
|
+
|
11
|
+
assert_sql 'SELECT _00.id, _01.member_id ' +
|
12
|
+
'FROM clients AS _00 ' +
|
13
|
+
'INNER JOIN members AS _01 ON _00.id = _01.id', result
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_inner_join
|
17
|
+
query = Qdsl::select { |t0, t1| [t0.id, t1.member_id] }.
|
18
|
+
from(@clients).
|
19
|
+
inner_join(@members).on { |t0, t1| t0.id.equals(t1.first_name) }
|
20
|
+
result = query.render
|
21
|
+
|
22
|
+
assert_sql 'SELECT _00.id, _01.member_id FROM clients AS _00 INNER JOIN members AS _01 ON _00.id = _01.first_name', result
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_multiple_inner_joins
|
26
|
+
query = Qdsl::select { |t0, t1, t2| [t0.id, t1.member_id, t2.name] }.
|
27
|
+
from(@clients).
|
28
|
+
inner_join(@members).on { |f, j| f.id.equals(j.first_name) }.
|
29
|
+
inner_join(@colours).on { |f, j| f.first_name.equals(j.name) }
|
30
|
+
result = query.render
|
31
|
+
|
32
|
+
assert_sql 'SELECT _00.id, _01.member_id, _02.name ' +
|
33
|
+
'FROM clients AS _00 ' +
|
34
|
+
'INNER JOIN members AS _01 ON _00.id = _01.first_name ' +
|
35
|
+
'INNER JOIN colours AS _02 ON _00.first_name = _02.name', result
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_multiple_inner_join_same
|
39
|
+
query = Qdsl::select { |t0, t1| [t0.id, t1.id] }.
|
40
|
+
from(@clients).
|
41
|
+
inner_join(@clients).on { |f, j| f.id.equals(j.first_name) }
|
42
|
+
result = query.render
|
43
|
+
|
44
|
+
assert_sql 'SELECT _00.id, _01.id FROM ' +
|
45
|
+
'clients AS _00 ' +
|
46
|
+
'INNER JOIN clients AS _01 ON _00.id = _01.first_name', result
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_multiple_inner_join_same_where
|
50
|
+
query = Qdsl::select { |t0, t1, t2| [t0.id, t1.id] }.
|
51
|
+
from(@clients).
|
52
|
+
inner_join(@clients).on { |f, j| f.id.equals(j.first_name) }.
|
53
|
+
inner_join(@clients).on { |f, j| f.id.equals(j.first_name) }.
|
54
|
+
where { |t0, t1, t2| t0.id.equals(t1.id) }
|
55
|
+
result = query.render
|
56
|
+
|
57
|
+
assert_sql 'SELECT _00.id, _01.id FROM ' +
|
58
|
+
'clients AS _00 ' +
|
59
|
+
'INNER JOIN clients AS _01 ON _00.id = _01.first_name ' +
|
60
|
+
'INNER JOIN clients AS _02 ON _00.id = _02.first_name ' +
|
61
|
+
'WHERE _00.id = _01.id', result
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
data/test/query_test.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require_relative 'minitest_helper'
|
2
|
+
|
3
|
+
module Qdsl
|
4
|
+
class QueryTest < Minitest::Test
|
5
|
+
def setup
|
6
|
+
@clients = Table.new(
|
7
|
+
:clients,
|
8
|
+
:id,
|
9
|
+
:first_name,
|
10
|
+
:last_name,
|
11
|
+
:value0,
|
12
|
+
:value1,
|
13
|
+
:value2,
|
14
|
+
:value3
|
15
|
+
)
|
16
|
+
@members = Table.new(
|
17
|
+
:members,
|
18
|
+
:id,
|
19
|
+
:first_name,
|
20
|
+
:middle_name,
|
21
|
+
:last_name,
|
22
|
+
:member_id
|
23
|
+
)
|
24
|
+
@colours = Table.new(
|
25
|
+
:colours,
|
26
|
+
:id,
|
27
|
+
:name
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def assert_sql(expected, actual)
|
32
|
+
expected.gsub! /\s*\n\s*/, ' '
|
33
|
+
expected.strip!
|
34
|
+
actual.gsub! /\s*\n\s*/, ' '
|
35
|
+
actual.strip!
|
36
|
+
assert_equal expected, actual
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
data/test/select_test.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
require_relative 'minitest_helper'
|
2
|
+
|
3
|
+
module Qdsl
|
4
|
+
class SelectTest < QueryTest
|
5
|
+
def test_single_table_select_single_column_string
|
6
|
+
query = Qdsl::select('id').from(@clients)
|
7
|
+
result = query.render
|
8
|
+
assert_sql 'SELECT _00.id FROM clients AS _00', result
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_single_table_select_single_column_symbol
|
12
|
+
query = Qdsl::select(:id).from(@clients)
|
13
|
+
result = query.render
|
14
|
+
assert_sql 'SELECT _00.id FROM clients AS _00', result
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_single_table_select_single_column_method
|
18
|
+
query = Qdsl::select { |t| t.id }.from(@clients)
|
19
|
+
result = query.render
|
20
|
+
assert_sql 'SELECT _00.id FROM clients AS _00', result
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_single_table_select_single_column_name_string
|
24
|
+
query = Qdsl::select { |t| t['id'] }.from(@clients)
|
25
|
+
result = query.render
|
26
|
+
assert_sql 'SELECT _00.id FROM clients AS _00', result
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_single_table_select_single_column_name_symbol
|
30
|
+
query = Qdsl::select { |t| t[:id] }.from(@clients)
|
31
|
+
result = query.render
|
32
|
+
assert_sql 'SELECT _00.id FROM clients AS _00', result
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_single_table_select_two_columns_string
|
36
|
+
query = Qdsl::select('id', 'first_name').from(@clients)
|
37
|
+
result = query.render
|
38
|
+
assert_sql 'SELECT _00.id, _00.first_name FROM clients AS _00', result
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_single_table_select_two_columns_symbol
|
42
|
+
query = Qdsl::select(:id, :first_name).from(@clients)
|
43
|
+
result = query.render
|
44
|
+
assert_sql 'SELECT _00.id, _00.first_name FROM clients AS _00', result
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_single_table_select_two_columns_method
|
48
|
+
query = Qdsl::select { |t| [t.id, t.first_name] }.from(@clients)
|
49
|
+
result = query.render
|
50
|
+
assert_sql 'SELECT _00.id, _00.first_name FROM clients AS _00', result
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_single_table_select_two_columns_name_string
|
54
|
+
query = Qdsl::select { |t| [t['id'], t['first_name']] }.from(@clients)
|
55
|
+
result = query.render
|
56
|
+
assert_sql 'SELECT _00.id, _00.first_name FROM clients AS _00', result
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_single_table_select_two_columns_name_symbol
|
60
|
+
query = Qdsl::select { |t| [t[:id], t[:first_name]] }.from(@clients)
|
61
|
+
result = query.render
|
62
|
+
assert_sql 'SELECT _00.id, _00.first_name FROM clients AS _00', result
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_two_tables_select_single_column_string
|
66
|
+
query = Qdsl::select('id').from(@clients).inner_join(@members).on { |t0, t1| t0.id.equals(t1.id) }
|
67
|
+
result = query.render
|
68
|
+
assert_sql 'SELECT _00.id FROM clients AS _00 INNER JOIN members AS _01 ON _00.id = _01.id', result
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_two_tables_select_single_column_symbol
|
72
|
+
query = Qdsl::select(:id).from(@clients).inner_join(@members).on { |t0, t1| t0.id.equals(t1.id) }
|
73
|
+
result = query.render
|
74
|
+
assert_sql 'SELECT _00.id FROM clients AS _00 INNER JOIN members AS _01 ON _00.id = _01.id', result
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_two_tables_select_single_column_method
|
78
|
+
query = Qdsl::select { |t0, t1| t0.id }.from(@clients).inner_join(@members).on { |t0, t1| t0.id.equals(t1.id) }
|
79
|
+
result = query.render
|
80
|
+
assert_sql 'SELECT _00.id FROM clients AS _00 INNER JOIN members AS _01 ON _00.id = _01.id', result
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_two_tables_select_single_column_name_string
|
84
|
+
query = Qdsl::select { |t0, t1| t0['id'] }.from(@clients).inner_join(@members).on { |t0, t1| t0.id.equals(t1.id) }
|
85
|
+
result = query.render
|
86
|
+
assert_sql 'SELECT _00.id FROM clients AS _00 INNER JOIN members AS _01 ON _00.id = _01.id', result
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_two_tables_select_single_column_name_symbol
|
90
|
+
query = Qdsl::select { |t0, t1| t0[:id] }.from(@clients).inner_join(@members).on { |t0, t1| t0.id.equals(t1.id) }
|
91
|
+
result = query.render
|
92
|
+
assert_sql 'SELECT _00.id FROM clients AS _00 INNER JOIN members AS _01 ON _00.id = _01.id', result
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require_relative 'minitest_helper'
|
2
|
+
|
3
|
+
module Qdsl
|
4
|
+
class SubqueryTest < QueryTest
|
5
|
+
def test_simple
|
6
|
+
subquery = Qdsl::select(:id, :first_name, :last_name).from(@clients)
|
7
|
+
query = Qdsl::select(:first_name).from(subquery)
|
8
|
+
|
9
|
+
assert_sql <<-EOL, query.render
|
10
|
+
SELECT _00.first_name
|
11
|
+
FROM (
|
12
|
+
SELECT _01.id, _01.first_name, _01.last_name
|
13
|
+
FROM clients AS _01
|
14
|
+
) AS _00
|
15
|
+
EOL
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_subquery_inner_join
|
19
|
+
subquery = Qdsl::select(:id, :first_name, :last_name).from(@clients)
|
20
|
+
query = Qdsl::select { |t0, t1| [t0.first_name, t1.last_name] }.
|
21
|
+
from(subquery).
|
22
|
+
inner_join(subquery).on { |t0, t1| t0.id.equals(t1.id) }
|
23
|
+
|
24
|
+
assert_sql <<-EOL, query.render
|
25
|
+
SELECT _00.first_name, _01.last_name
|
26
|
+
FROM (
|
27
|
+
SELECT _02.id, _02.first_name, _02.last_name
|
28
|
+
FROM clients AS _02
|
29
|
+
) AS _00
|
30
|
+
INNER JOIN (
|
31
|
+
SELECT _03.id, _03.first_name, _03.last_name
|
32
|
+
FROM clients AS _03
|
33
|
+
) AS _01
|
34
|
+
ON _00.id = _01.id
|
35
|
+
EOL
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_subquery_inner_join_where
|
39
|
+
subquery = Qdsl::select(:id, :first_name, :last_name).from(@clients)
|
40
|
+
query = Qdsl::select { |t0, t1| [t0.first_name, t1.last_name] }.
|
41
|
+
from(subquery).
|
42
|
+
inner_join(subquery).on { |t0, t1| t0.id.equals(t1.id) }.
|
43
|
+
where { |t0, t1| t0.first_name.equals('str0').and(t1.last_name.equals('str1')) }
|
44
|
+
|
45
|
+
context = Context.new
|
46
|
+
assert_sql <<-EOL, query.render(context)
|
47
|
+
SELECT _00.first_name, _01.last_name
|
48
|
+
FROM (
|
49
|
+
SELECT _02.id, _02.first_name, _02.last_name
|
50
|
+
FROM clients AS _02
|
51
|
+
) AS _00
|
52
|
+
INNER JOIN (
|
53
|
+
SELECT _03.id, _03.first_name, _03.last_name
|
54
|
+
FROM clients AS _03
|
55
|
+
) AS _01
|
56
|
+
ON _00.id = _01.id
|
57
|
+
WHERE (_00.first_name = :_param00) AND (_01.last_name = :_param01)
|
58
|
+
EOL
|
59
|
+
|
60
|
+
assert_equal 2, context.parameters.size
|
61
|
+
assert_equal 'str0', context.parameters['_param00']
|
62
|
+
assert_equal 'str1', context.parameters['_param01']
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
data/test/where_test.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'minitest_helper'
|
2
|
+
|
3
|
+
module Qdsl
|
4
|
+
class WhereTest < QueryTest
|
5
|
+
def test_simple_where
|
6
|
+
query = Qdsl::select(:first_name).
|
7
|
+
from(@clients).
|
8
|
+
where { |t| t.id.equals('Cook') }
|
9
|
+
|
10
|
+
context = Context.new
|
11
|
+
result = query.render(context)
|
12
|
+
|
13
|
+
assert_sql 'SELECT _00.first_name ' +
|
14
|
+
'FROM clients AS _00 ' +
|
15
|
+
'WHERE _00.id = :_param00', result
|
16
|
+
assert_equal 1, context.parameters.size
|
17
|
+
assert_equal 'Cook', context.parameters['_param00']
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
metadata
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: qdsl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Richard Cook
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Think of it as "hygienic macros for SQL
|
56
|
+
email:
|
57
|
+
- rcook@rcook.org
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- ".travis.yml"
|
64
|
+
- Gemfile
|
65
|
+
- LICENSE.txt
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- lib/and.rb
|
69
|
+
- lib/boolean.rb
|
70
|
+
- lib/column.rb
|
71
|
+
- lib/context.rb
|
72
|
+
- lib/equals.rb
|
73
|
+
- lib/expression.rb
|
74
|
+
- lib/funky_proxy.rb
|
75
|
+
- lib/inner_join.rb
|
76
|
+
- lib/inner_join_builder.rb
|
77
|
+
- lib/or.rb
|
78
|
+
- lib/qdsl.rb
|
79
|
+
- lib/qdsl/version.rb
|
80
|
+
- lib/select.rb
|
81
|
+
- lib/table.rb
|
82
|
+
- lib/table_query.rb
|
83
|
+
- qdsl.gemspec
|
84
|
+
- test/column_test.rb
|
85
|
+
- test/context_test.rb
|
86
|
+
- test/end_to_end_test.rb
|
87
|
+
- test/expression_test.rb
|
88
|
+
- test/inner_join_test.rb
|
89
|
+
- test/minitest_helper.rb
|
90
|
+
- test/query_test.rb
|
91
|
+
- test/select_test.rb
|
92
|
+
- test/subquery_test.rb
|
93
|
+
- test/where_test.rb
|
94
|
+
homepage: ''
|
95
|
+
licenses:
|
96
|
+
- MIT
|
97
|
+
metadata: {}
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 2.4.5
|
115
|
+
signing_key:
|
116
|
+
specification_version: 4
|
117
|
+
summary: SQL query builder DSL for Ruby
|
118
|
+
test_files:
|
119
|
+
- test/column_test.rb
|
120
|
+
- test/context_test.rb
|
121
|
+
- test/end_to_end_test.rb
|
122
|
+
- test/expression_test.rb
|
123
|
+
- test/inner_join_test.rb
|
124
|
+
- test/minitest_helper.rb
|
125
|
+
- test/query_test.rb
|
126
|
+
- test/select_test.rb
|
127
|
+
- test/subquery_test.rb
|
128
|
+
- test/where_test.rb
|