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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d324609dd93210feb39bb7b46739c46cfbc0662f
4
- data.tar.gz: cb52c14ff194b1222eaed3b22b0ad938dc7a1914
3
+ metadata.gz: ea885fee238d20c64364425cad9315b36a857c5a
4
+ data.tar.gz: 213baa1bff13a230263f742537667474e54fecee
5
5
  SHA512:
6
- metadata.gz: 64af342ed59b17cd4912168f60fe61f46fae2e1da4e9077095736b96c917953098a700851c7c877f263d0ddddfc9ddb3a4d683b90313d7d106d778461a7478c8
7
- data.tar.gz: 234cfb876a6f5119807075fcceac54ec63b819af7b733d97964252e43385851af2052ff25928167f376a67383c0dae1e376e5aca60588791fde5ad4cc667e73e
6
+ metadata.gz: 05ecfc437a608a3e87369501f7ab3ffba0373e731752bdaa1fd4d89d7736e6479c33eea3e3ae589e6ae9ba7fb12f699e9100b3d88c763efecbda93e0c1368ae1
7
+ data.tar.gz: 82059189fb7904376ca5d03bd9ceca30fe77e47f67ab61280c49f66b4f9764ac3b34b1710c6f5791f9e66c4d2df64dd2b456309a12ceba636247880714de6409
data/HISTORY CHANGED
@@ -1,3 +1,8 @@
1
+ # 0.11
2
+
3
+ + #anchor now only affects the interpretation of #on, doesn't modify return
4
+ value.
5
+ + #hoist is the opposite of #anchor - it reverts to the original entity.
1
6
 
2
7
  # 0.10
3
8
  + Datamapper interop is improved (also in #order_by)
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.10.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(entity, k, join_field(v)) })
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
- @entity = @last_join
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
- Joins are joined using AND.
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.10.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-22 00:00:00.000000000 Z
12
+ date: 2014-08-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: arel