flounder 0.10.0 → 0.11.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/HISTORY +5 -0
- data/flounder.gemspec +1 -1
- data/lib/flounder/query/select.rb +13 -2
- data/qed/flounder.sql +3 -2
- data/qed/index.md +0 -50
- data/qed/joins.md +65 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea885fee238d20c64364425cad9315b36a857c5a
|
4
|
+
data.tar.gz: 213baa1bff13a230263f742537667474e54fecee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 05ecfc437a608a3e87369501f7ab3ffba0373e731752bdaa1fd4d89d7736e6479c33eea3e3ae589e6ae9ba7fb12f699e9100b3d88c763efecbda93e0c1368ae1
|
7
|
+
data.tar.gz: 82059189fb7904376ca5d03bd9ceca30fe77e47f67ab61280c49f66b4f9764ac3b34b1710c6f5791f9e66c4d2df64dd2b456309a12ceba636247880714de6409
|
data/HISTORY
CHANGED
data/flounder.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "flounder"
|
5
|
-
s.version = '0.
|
5
|
+
s.version = '0.11.0'
|
6
6
|
s.summary = "Flounder is a way to write SQL simply in Ruby. It deals with everything BUT object relational mapping. "
|
7
7
|
s.email = "kaspar.schiess@technologyastronauts.ch"
|
8
8
|
s.homepage = "https://bitbucket.org/technologyastronauts/oss_flounder"
|
@@ -14,6 +14,9 @@ module Flounder::Query
|
|
14
14
|
@projection_prefixes = Hash.new
|
15
15
|
@default_projection = []
|
16
16
|
|
17
|
+
# by default, joins are made to the entity that you start the query with
|
18
|
+
@join_entity = entity
|
19
|
+
|
17
20
|
add_fields_to_default from_entity
|
18
21
|
|
19
22
|
manager.from from_entity.table
|
@@ -30,6 +33,8 @@ module Flounder::Query
|
|
30
33
|
# prefix during this query.
|
31
34
|
attr_reader :projection_prefixes
|
32
35
|
|
36
|
+
attr_reader :join_entity, :last_join
|
37
|
+
|
33
38
|
def _join join_node, entity
|
34
39
|
entity = convert_to_entity(entity)
|
35
40
|
|
@@ -67,11 +72,17 @@ module Flounder::Query
|
|
67
72
|
|
68
73
|
def on join_conditions
|
69
74
|
manager.on(
|
70
|
-
*join_conditions.map { |(k, v)| transform_tuple(
|
75
|
+
*join_conditions.map { |(k, v)| transform_tuple(
|
76
|
+
join_entity, k, join_field(v)) })
|
71
77
|
self
|
72
78
|
end
|
79
|
+
|
73
80
|
def anchor
|
74
|
-
@
|
81
|
+
@join_entity = last_join
|
82
|
+
self
|
83
|
+
end
|
84
|
+
def hoist
|
85
|
+
@join_entity = entity
|
75
86
|
self
|
76
87
|
end
|
77
88
|
|
data/qed/flounder.sql
CHANGED
@@ -30,11 +30,12 @@ DROP TABLE IF EXISTS "comments" CASCADE;
|
|
30
30
|
CREATE TABLE "comments" (
|
31
31
|
"id" serial PRIMARY KEY,
|
32
32
|
"post_id" int NOT NULL REFERENCES posts("id"),
|
33
|
-
"text" text NOT NULL
|
33
|
+
"text" text NOT NULL,
|
34
|
+
"author_id" int not null REFERENCES users("id")
|
34
35
|
);
|
35
36
|
|
36
37
|
BEGIN;
|
37
|
-
INSERT INTO "comments" (post_id, text) VALUES (1, 'A silly comment.');
|
38
|
+
INSERT INTO "comments" (post_id, text, author_id) VALUES (1, 'A silly comment.', 1);
|
38
39
|
COMMIT;
|
39
40
|
|
40
41
|
-- import using
|
data/qed/index.md
CHANGED
@@ -72,56 +72,6 @@ By default, symbols are interpreted as field names in the entity that you start
|
|
72
72
|
assert generates_sql("SELECT [fields] FROM \"users\" WHERE \"posts\".\"id\" = 1")
|
73
73
|
~~~
|
74
74
|
|
75
|
-
# Some JOINs
|
76
|
-
|
77
|
-
Here are some non-crazy joins that also work.
|
78
|
-
|
79
|
-
~~~ruby
|
80
|
-
domain[:users].join(domain[:posts]).on(:id => :user_id).
|
81
|
-
assert generates_sql(%Q(SELECT [fields] FROM "users" INNER JOIN "posts" ON "users"."id" = "posts"."user_id"))
|
82
|
-
|
83
|
-
domain[:users].outer_join(domain[:posts]).on(:id => :user_id).
|
84
|
-
assert generates_sql(%Q(SELECT [fields] FROM "users" LEFT OUTER JOIN "posts" ON "users"."id" = "posts"."user_id"))
|
85
|
-
~~~
|
86
|
-
|
87
|
-
You might want to drop the calls to domain.
|
88
|
-
|
89
|
-
~~~ruby
|
90
|
-
domain[:users].join(:posts).on(:id => :user_id).
|
91
|
-
assert generates_sql(
|
92
|
-
%Q(SELECT [fields] FROM "users" INNER JOIN "posts" ON "users"."id" = "posts"."user_id"))
|
93
|
-
~~~
|
94
|
-
|
95
|
-
Joining presents an interesting dilemma. There are two ways of joining things together, given three tables. The sequence A.B.C might mean to join A to B and C; it might also be interpreted to mean to join A to B and B to C. Here's how we solve this.
|
96
|
-
|
97
|
-
~~~ruby
|
98
|
-
domain[:users].
|
99
|
-
join(domain[:posts]).on(:id => :user_id).
|
100
|
-
join(domain[:comments]).on(:id => :post_id).
|
101
|
-
assert generates_sql(%Q(SELECT [fields] FROM "users" INNER JOIN "posts" ON "users"."id" = "posts"."user_id" INNER JOIN "comments" ON "users"."id" = "comments"."post_id"))
|
102
|
-
~~~
|
103
|
-
|
104
|
-
So just doing `A.B.C` will give you the first of the above possibilities. Here's how to achive the second effect.
|
105
|
-
|
106
|
-
~~~ruby
|
107
|
-
domain[:users].
|
108
|
-
join(domain[:posts]).on(:id => :user_id).anchor.
|
109
|
-
join(domain[:comments]).on(:id => :post_id).
|
110
|
-
assert generates_sql(%Q(SELECT [fields] FROM "users" INNER JOIN "posts" ON "users"."id" = "posts"."user_id" INNER JOIN "comments" ON "posts"."id" = "comments"."post_id"))
|
111
|
-
~~~
|
112
|
-
|
113
|
-
The call to `#anchor` anchors all further joins at that point.
|
114
|
-
|
115
|
-
# Ordering records
|
116
|
-
|
117
|
-
~~~ruby
|
118
|
-
domain[:users].where(id: 2013).order_by(domain[:users][:id]).
|
119
|
-
assert generates_sql(%Q(SELECT [fields] FROM "users" WHERE "users"."id" = 2013 ORDER BY "users"."id"))
|
120
|
-
|
121
|
-
domain[:users].order_by(:id).
|
122
|
-
assert generates_sql(%Q(SELECT [fields] FROM "users" ORDER BY "users"."id"))
|
123
|
-
~~~
|
124
|
-
|
125
75
|
# Selective projection
|
126
76
|
|
127
77
|
~~~ruby
|
data/qed/joins.md
CHANGED
@@ -11,7 +11,7 @@ Joins are correctly created.
|
|
11
11
|
row.post.id.assert == 1
|
12
12
|
~~~
|
13
13
|
|
14
|
-
|
14
|
+
Join conditions are joined using `AND`.
|
15
15
|
|
16
16
|
~~~ruby
|
17
17
|
query = domain[:posts].
|
@@ -19,3 +19,67 @@ Joins are … joined using AND.
|
|
19
19
|
|
20
20
|
query.assert generates_sql(%Q(SELECT [fields] FROM "posts" INNER JOIN "users" ON "posts"."user_id" = "users"."id" AND "posts"."strange_example_id" = "users"."id"))
|
21
21
|
~~~
|
22
|
+
|
23
|
+
You might want to drop the calls to domain.
|
24
|
+
|
25
|
+
~~~ruby
|
26
|
+
domain[:users].join(:posts).on(:id => :user_id).
|
27
|
+
assert generates_sql(
|
28
|
+
%Q(SELECT [fields] FROM "users" INNER JOIN "posts" ON "users"."id" = "posts"."user_id"))
|
29
|
+
~~~
|
30
|
+
|
31
|
+
|
32
|
+
# Anchoring
|
33
|
+
|
34
|
+
Joining presents an interesting dilemma. There are two ways of joining things together, given three tables. The sequence A.B.C might mean to join A to B and C; it might also be interpreted to mean to join A to B and B to C. Here's how we solve this.
|
35
|
+
|
36
|
+
~~~ruby
|
37
|
+
domain[:users].
|
38
|
+
join(domain[:posts]).on(:id => :user_id).
|
39
|
+
join(domain[:comments]).on(:id => :post_id).
|
40
|
+
assert generates_sql(%Q(SELECT [fields] FROM "users" INNER JOIN "posts" ON "users"."id" = "posts"."user_id" INNER JOIN "comments" ON "users"."id" = "comments"."post_id"))
|
41
|
+
~~~
|
42
|
+
|
43
|
+
So just doing `A.B.C` will give you the first of the above possibilities. Here's how to achive the second effect.
|
44
|
+
|
45
|
+
~~~ruby
|
46
|
+
users.
|
47
|
+
join(:posts).on(:id => :user_id).anchor.
|
48
|
+
join(:comments).on(:id => :post_id).
|
49
|
+
assert generates_sql(%Q(SELECT [fields] FROM "users" INNER JOIN "posts" ON "users"."id" = "posts"."user_id" INNER JOIN "comments" ON "posts"."id" = "comments"."post_id"))
|
50
|
+
~~~
|
51
|
+
|
52
|
+
During the construction of the query, flounder keeps track of what table joins are relative to. That table also provides field resolution for the left hand side of the `#on` clause:
|
53
|
+
|
54
|
+
A.join(B).on(:a_field => :b_field).
|
55
|
+
join(C).on(:a_field => :c_field)
|
56
|
+
|
57
|
+
By default, A in the above example will always be the entity that you started the query with. To anchor a join on a different entity, you will need to join that entity in and then end the join with a call to `#anchor`:
|
58
|
+
|
59
|
+
A.join(B).on(:a_field => :b_field).anchor.
|
60
|
+
join(C).on(:b_field => :c_field)
|
61
|
+
|
62
|
+
To get rid of the anchor, you will need to call `#hoist`. This will always reset the left side of all further joins to the entity that you started the
|
63
|
+
query with:
|
64
|
+
|
65
|
+
A.join(B).on(:a_field => :b_field).anchor.
|
66
|
+
join(C).on(:b_field => :c_field).hoist.
|
67
|
+
join(D).on(:a_field => :d_field)
|
68
|
+
|
69
|
+
Here's a real usage example.
|
70
|
+
|
71
|
+
~~~ruby
|
72
|
+
query = users.
|
73
|
+
join(:posts).on(:id => :user_id).anchor.
|
74
|
+
join(:comments).on(:id => :post_id).hoist.
|
75
|
+
join(comments.as(:other_comments, :other_comment)).on(:id => :author_id)
|
76
|
+
|
77
|
+
query.assert generates_sql("SELECT [fields] FROM \"users\" INNER JOIN \"posts\" ON \"users\".\"id\" = \"posts\".\"user_id\" INNER JOIN \"comments\" ON \"posts\".\"id\" = \"comments\".\"post_id\" INNER JOIN \"comments\" \"other_comments\" ON \"users\".\"id\" = \"other_comments\".\"author_id\"")
|
78
|
+
~~~
|
79
|
+
|
80
|
+
Results should also be correct:
|
81
|
+
|
82
|
+
~~~ruby
|
83
|
+
row = query.first
|
84
|
+
row.other_comment.assert == row.comment
|
85
|
+
~~~
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flounder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kaspar Schiess
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-08-
|
12
|
+
date: 2014-08-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: arel
|