flounder 0.14.0 → 0.15.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: 5cc0f113f0d254c49373affa1869e3f8fc03a3fb
4
- data.tar.gz: 8ff7ed39500492b2f965ff7e1d7c1acc2a7f2473
3
+ metadata.gz: 9f4ec6bd3fa4f14eccf7efa8d3aed4e81a3d8e08
4
+ data.tar.gz: cba009f5dc2b519b4636b643b7ac1d02bd9a493c
5
5
  SHA512:
6
- metadata.gz: a0e855f62812d513ae1ca5bfb0d04d0a313c5f9d0f7c42d076de31625a60b344dd32a05e8f9cfb674346e6db7c964ea157c0f2c50f91f5dce333226612191a23
7
- data.tar.gz: bc99c6125d8e890eb89f1db3ab1ad326e50f0a2d13950a4efed4089f4c209773e3d79dc2513862c33a82788d130c9484c60cc7b7a09832efd55a10a91c55b829
6
+ metadata.gz: 81a859be8701dc7aa497f17949ed8bb8db93cb22daaba9c24796d5d225ef4cfd936174f132648bde9755d67b4aa15c92db2c09e404fec28280e4db62d3fa302b
7
+ data.tar.gz: 85f24647fe87ddd7a788df1f0722811fd828d4e0c5c570c6ac295e40d9ef6d4e5fb3ead5526a20bf35566f2132638c7b355ba0e1291697e79c20f8c63a6d9c38
data/HISTORY CHANGED
@@ -1,4 +1,7 @@
1
1
 
2
+ # 0.15
3
+ + Condition piece generator - use this to create OR expressions: Entity#cond
4
+
2
5
  # 0.13
3
6
  + Expression generator (Domain#expr).
4
7
 
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.14.0'
5
+ s.version = '0.15.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"
@@ -50,9 +50,12 @@ module Flounder
50
50
  end
51
51
 
52
52
  # Builds an SQL expression.
53
+ #
54
+ # domain.expr { concat('1', '2') }
53
55
  #
54
- def expr context=self, &block
55
- Expression::Builder.new(self, context).call(&block)
56
+ def expr &block
57
+ builder = Expression::Builder.new(self)
58
+ builder.call(&block)
56
59
  end
57
60
 
58
61
  # Returns an aggregate of all query wall clock times. Please see
@@ -66,6 +66,15 @@ module Flounder
66
66
  end
67
67
  alias to_s inspect
68
68
 
69
+ # Builds a condition part or a general SQL expression.
70
+ #
71
+ # entity.cond(a: 1)
72
+ #
73
+ def cond *conditions
74
+ builder = Expression::Builder.new(domain)
75
+ builder.interpret_conditions(self, conditions)
76
+ end
77
+
69
78
  # Starts a new select query and yields it to the block. Note that you don't
70
79
  # need to call this method to obtain a select query - any of the methods
71
80
  # on Query::Select should work on the entity and return a new query object.
@@ -11,6 +11,12 @@ module Flounder::Expression
11
11
  def cast type
12
12
  Cast.new(@domain, type, self)
13
13
  end
14
+ def | expr
15
+ BinaryOp.new(@domain, 'OR', self, expr)
16
+ end
17
+ def & expr
18
+ BinaryOp.new(@domain, 'AND', self, expr)
19
+ end
14
20
 
15
21
  def eval argument
16
22
  case argument
@@ -83,18 +89,70 @@ module Flounder::Expression
83
89
  end
84
90
  end
85
91
 
92
+ class ConditionBit < Expr
93
+ def initialize domain, engine, bit
94
+ super(domain)
95
+ @engine = engine
96
+ @bit = bit
97
+ end
98
+
99
+ attr_accessor :bit, :engine
100
+
101
+ def to_sql
102
+ bit.to_sql(engine)
103
+ end
104
+ end
105
+
106
+ class BinaryOp < Expr
107
+ def initialize domain, op_string, *terms
108
+ super(domain)
109
+
110
+ @op_string = op_string
111
+ @terms = terms
112
+ end
113
+
114
+ attr_accessor :op_string
115
+
116
+ def concat term
117
+ @terms << term
118
+ end
119
+
120
+ def to_sql
121
+ "(" +
122
+ @terms.
123
+ map { |t| eval(t) }.
124
+ join(" #{op_string} ") +
125
+ ")"
126
+ end
127
+ end
128
+
86
129
  class Builder
87
- def initialize domain, context
130
+ def initialize domain
88
131
  @domain = domain
89
- @context = context
90
132
  end
91
133
 
92
- attr_accessor :domain, :context
134
+ attr_accessor :domain
93
135
 
94
136
  def call &block
95
137
  instance_eval(&block)
96
138
  end
97
139
 
140
+ # Conditions are interpreted relative to an entity.
141
+ def interpret_conditions entity, conditions
142
+ # The method we need is in there...
143
+ query = Flounder::Query::Select.new(domain, entity)
144
+ engine = Flounder::Engine.new(domain.connection_pool)
145
+
146
+ and_expr = BinaryOp.new(domain, 'AND')
147
+
148
+ # TODO parse_conditions and its call tree is not really something that
149
+ # belongs into the query object - we should create a new abstraction here.
150
+ query.parse_conditions(*conditions) { |bit|
151
+ and_expr.concat(ConditionBit.new(domain, engine, bit)) }
152
+
153
+ and_expr
154
+ end
155
+
98
156
  def respond_to? sym, include_all=false
99
157
  true
100
158
  end
@@ -81,14 +81,6 @@ module Flounder::Query
81
81
  domain.log_bm measure
82
82
  end
83
83
 
84
- private
85
- # Prepares a kick - meaning an execution on the database or a transform
86
- # to an sql string. Ready Set...
87
- #
88
- def prepare_kick
89
- # should be overridden
90
- end
91
-
92
84
  # Parses a conditions array like it is found with #where and #having and
93
85
  # calls the block for each condition bit. Returns self.
94
86
  #
@@ -126,6 +118,14 @@ module Flounder::Query
126
118
  end
127
119
  return self
128
120
  end
121
+
122
+ private
123
+ # Prepares a kick - meaning an execution on the database or a transform
124
+ # to an sql string. Ready Set...
125
+ #
126
+ def prepare_kick
127
+ # should be overridden
128
+ end
129
129
 
130
130
  # Rewrites a statement that contains bind placeholders like '$1' to
131
131
  # contain placeholders starting at offset+1. Also checks that no
data/qed/expressions.md CHANGED
@@ -4,8 +4,14 @@
4
4
  Flounder also helps with the writing of complex SQL expressions.
5
5
 
6
6
  ~~~ruby
7
- users = domain[:users]
8
- domain.expr { a(b(c(1, ' ', users[:id]))) }.cast('int').as(:foo).
9
- assert generates_sql("a(b(c(1, ' ', \"users\".\"id\")))::int AS foo")
7
+ users = domain[:users]
8
+ domain.expr { a(b(c(1, ' ', users[:id]))) }.cast('int').as(:foo).
9
+ assert generates_sql("a(b(c(1, ' ', \"users\".\"id\")))::int AS foo")
10
10
  ~~~
11
11
 
12
+ You can also pass a condition-like hash to expr directly, it will return a Ruby object that allows for chaining.
13
+
14
+ ~~~ruby
15
+ (users.cond(:posts, a: 1) | users.cond(a: 2)).
16
+ assert generates_sql("((\"posts\".\"a\" = 1) OR (\"users\".\"a\" = 2))")
17
+ ~~~
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.14.0
4
+ version: 0.15.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-10-02 00:00:00.000000000 Z
12
+ date: 2014-10-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: arel