db-model 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,221 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require_relative 'literal'
24
+
25
+ module DB
26
+ module Model
27
+ module Statement
28
+ module Predicate
29
+ class Empty
30
+ def append_to(statement)
31
+ statement.literal(true)
32
+ end
33
+
34
+ def + other
35
+ other
36
+ end
37
+
38
+ def eql?(other)
39
+ self.class.eql?(other.class)
40
+ end
41
+
42
+ def hash
43
+ self.class.hash
44
+ end
45
+ end
46
+
47
+ EMPTY = Empty.new
48
+
49
+ class Between
50
+ def initialize(key, minimum, maximum)
51
+ @key = key
52
+ @minimum = minimum
53
+ @maximum = maximum
54
+ end
55
+
56
+ attr :key
57
+ attr :minimum
58
+ attr :maximum
59
+
60
+ def append_to(statement)
61
+ @key.append_to(statement)
62
+ statement.clause("BETWEEN")
63
+ @minimum.append_to(statement)
64
+ statement.clause("AND")
65
+ @maximum.append_to(statement)
66
+ end
67
+
68
+ def eql?(other)
69
+ self.class.eql?(other.class) && self.key.eql?(other.key) && self.minimum.eql?(other.minimum) && self.maximum.eql?(other.maximum)
70
+ end
71
+
72
+ def hash
73
+ [self.class, @key, @minimum, @maximum].hash
74
+ end
75
+ end
76
+
77
+ class Binary
78
+ def initialize(key, operator, value)
79
+ @key = key
80
+ @operator = operator
81
+ @value = value
82
+ end
83
+
84
+ attr :key
85
+ attr :operator
86
+ attr :value
87
+
88
+ def append_to(statement)
89
+ @key.append_to(statement)
90
+ statement.clause(@operator)
91
+ @value.append_to(statement)
92
+ end
93
+
94
+ def eql?(other)
95
+ self.class.eql?(other.class) && self.key.eql?(other.key) && self.operator.eql?(other.operator) && self.value.eql?(other.value)
96
+ end
97
+
98
+ def hash
99
+ [self.class, @key, @operator, @value].hash
100
+ end
101
+ end
102
+
103
+ class Null
104
+ def initialize(key)
105
+ @key = key
106
+ end
107
+
108
+ attr :key
109
+
110
+ def append_to(statement)
111
+ @key.append_to(statement)
112
+ statement.clause("IS NULL")
113
+ end
114
+
115
+ def eql?(other)
116
+ self.class.eql?(other.class) && self.key.eql?(other.key)
117
+ end
118
+
119
+ def hash
120
+ [self.class, @key].hash
121
+ end
122
+ end
123
+
124
+ class Composite
125
+ def self.for(predicates)
126
+ if predicates.size == 0
127
+ return EMPTY
128
+ else
129
+ return self.new(predicates)
130
+ end
131
+ end
132
+
133
+ def initialize(predicates, operator = "AND")
134
+ @predicates = predicates
135
+ @operator = operator
136
+ end
137
+
138
+ attr :predicates
139
+ attr :operator
140
+
141
+ def append_to(statement)
142
+ first = true
143
+
144
+ statement.clause("(")
145
+
146
+ @predicates.each_with_index do |predicate, index|
147
+ statement.clause(@operator) unless first
148
+ first = false
149
+
150
+ predicate.append_to(statement)
151
+ end
152
+
153
+ statement.clause(")")
154
+ end
155
+
156
+ def + other
157
+ Composite.new([self, other])
158
+ end
159
+
160
+ def & other
161
+ Composite.new([self, other], "AND")
162
+ end
163
+
164
+ def | other
165
+ Composite.new([self, other], "OR")
166
+ end
167
+
168
+ def eql?(other)
169
+ self.class.eql?(other.class) && self.predicates.eql?(other.predicates)
170
+ end
171
+
172
+ def hash
173
+ [self.class, @predicates, @operator].hash
174
+ end
175
+ end
176
+
177
+ def self.coerce(key, value)
178
+ case value
179
+ when Array
180
+ Binary.new(key, "IN", Tuple.new(value))
181
+ when Range
182
+ if value.min.nil?
183
+ if value.exclude_end?
184
+ Binary.new(key, "<", Literal.new(value.max))
185
+ else
186
+ Binary.new(key, "<=", Literal.new(value.max))
187
+ end
188
+ elsif value.max.nil?
189
+ if value.exclude_end?
190
+ Binary.new(key, ">", Literal.new(value.max))
191
+ else
192
+ Binary.new(key, ">=", Literal.new(value.max))
193
+ end
194
+ else
195
+ if value.exclude_end?
196
+ Composite.new([
197
+ Binary.new(key, ">", Literal.new(value.min)),
198
+ Binary.new(key, "<", Literal.new(value.max))
199
+ ])
200
+ else
201
+ Between.new(key, Literal.new(value.min), Literal.new(value.max))
202
+ end
203
+ end
204
+ when nil
205
+ Null.new(key)
206
+ else
207
+ Binary.new(key, "=", Literal.new(value))
208
+ end
209
+ end
210
+
211
+ def self.where(*arguments, **options, &block)
212
+ Composite.for(
213
+ options.map do |key, value|
214
+ coerce(Identifier.coerce(key), value)
215
+ end
216
+ )
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ module DB
24
+ module Model
25
+ module Statement
26
+ class Replace
27
+ def initialize(source, fields, values)
28
+ @source = source
29
+ @fields = fields
30
+ @values = values
31
+ end
32
+
33
+ def to_sql(context)
34
+ statement = context.query("REPLACE INTO")
35
+
36
+ statement.identifier(@source.type)
37
+
38
+ statement.clause("(")
39
+ @fields.append_to(statement)
40
+ statement.clause(") VALUES")
41
+
42
+ @values.append_to(statement)
43
+
44
+ statement.clause("RETURNING *")
45
+
46
+ return statement
47
+ end
48
+
49
+ def to_a(context, klass)
50
+ to_sql(context).call do |connection|
51
+ result = connection.next_result
52
+ keys = result.field_names.map(&:to_sym)
53
+
54
+ result.map do |row|
55
+ klass.new(context, keys.zip(row).to_h)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ module DB
24
+ module Model
25
+ module Statement
26
+ class Select
27
+ def initialize(source, fields: nil, where: nil, limit: nil)
28
+ @source = source
29
+ @fields = fields
30
+ @where = where
31
+ @limit = limit
32
+ end
33
+
34
+ def append_to(statement, limit: @limit)
35
+ statement = statement.clause("SELECT")
36
+
37
+ if @fields
38
+ @fields.append_to(statement)
39
+ else
40
+ statement.clause("*")
41
+ end
42
+
43
+ statement.clause("FROM")
44
+ statement.identifier(@source.type)
45
+
46
+ if @where
47
+ statement.clause "WHERE"
48
+ @where.append_to(statement)
49
+ end
50
+
51
+ limit&.append_to(statement)
52
+
53
+ return statement
54
+ end
55
+
56
+ def to_sql(context)
57
+ self.append_to(context)
58
+ end
59
+
60
+ def to_a(context, cache = nil)
61
+ to_sql(context).call do |connection|
62
+ result = connection.next_result
63
+
64
+ return apply(context, result, cache)
65
+ end
66
+ end
67
+
68
+ def apply(context, result, cache = nil)
69
+ keys = result.field_names.map(&:to_sym)
70
+
71
+ result.map do |row|
72
+ @source.new(context, keys.zip(row).to_h, cache)
73
+ end
74
+ end
75
+
76
+ def each(context, cache = nil)
77
+ to_sql(context).call do |connection|
78
+ result = connection.next_result
79
+ keys = result.field_names.map(&:to_sym)
80
+
81
+ result.each do |row|
82
+ yield @source.new(context, keys.zip(row).to_h, cache)
83
+ end
84
+ end
85
+ end
86
+
87
+ def first(context, count, cache = nil)
88
+ limit = @limit&.first(count) || Limit.new(count)
89
+
90
+ append_to(context, limit: limit).call do |connection|
91
+ result = connection.next_result
92
+
93
+ return apply(context, result, cache)
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ module DB
24
+ module Model
25
+ module Statement
26
+ class Truncate
27
+ def initialize(source)
28
+ @source = source
29
+ end
30
+
31
+ def to_sql(context)
32
+ statement = context.clause("TRUNCATE TABLE")
33
+
34
+ statement.identifier(@source.type)
35
+
36
+ return statement
37
+ end
38
+
39
+ def call(context)
40
+ to_sql(context).call
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ module DB
24
+ module Model
25
+ module Statement
26
+ class Tuple
27
+ def initialize(values)
28
+ @values = values
29
+ end
30
+
31
+ attr :values
32
+
33
+ def append_to(statement)
34
+ first = true
35
+
36
+ statement.clause("(")
37
+ @values.each do |value|
38
+ statement.clause(",") unless first
39
+ first = false
40
+
41
+ statement.literal(value)
42
+ end
43
+ statement.clause(")")
44
+
45
+ return statement
46
+ end
47
+
48
+ def eql?(other)
49
+ self.class.eql?(other.class) && self.values.eql?(other.values)
50
+ end
51
+
52
+ def hash
53
+ [self.class, @values].hash
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ module DB
24
+ module Model
25
+ module Statement
26
+ class Update
27
+ def initialize(source, assignment, where)
28
+ @source = source
29
+ @assignment = assignment
30
+ @where = where
31
+ end
32
+
33
+ def to_sql(context)
34
+ statement = context.query("UPDATE")
35
+
36
+ statement.identifier(@source.type)
37
+
38
+ @assignment.append_to(statement)
39
+
40
+ if @where
41
+ statement.clause "WHERE"
42
+ @where.append_to(statement)
43
+ end
44
+
45
+ return statement
46
+ end
47
+
48
+ def call(context)
49
+ to_sql(context).call
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require_relative 'relation'
24
+ require_relative 'statement/truncate'
25
+
26
+ module DB
27
+ module Model
28
+ class Table < Relation
29
+ def cache_key
30
+ [@model]
31
+ end
32
+
33
+ def truncate
34
+ Statement::Truncate.new(@model).call(@context)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ module DB
24
+ module Model
25
+ VERSION = "0.3.0"
26
+ end
27
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require_relative 'statement/select'
24
+ require_relative 'statement/predicate'
25
+
26
+ require_relative 'countable'
27
+
28
+ module DB
29
+ module Model
30
+ class Where
31
+ def initialize(context, model, *arguments, **options, &block)
32
+ @context = context
33
+ @model = model
34
+ @predicate = Statement::Predicate.where(*arguments, **options, &block)
35
+
36
+ @select = nil
37
+ end
38
+
39
+ attr_accessor :predicate
40
+
41
+ include Countable, Deletable
42
+
43
+ def or(*arguments, **options, &block)
44
+ @predicate |= Statement::Predicate.where(*arguments, **options, &block)
45
+
46
+ return self
47
+ end
48
+
49
+ def and(*arguments, **options, &block)
50
+ @predicate &= Statement::Predicate.where(*arguments, **options, &block)
51
+
52
+ return self
53
+ end
54
+
55
+ def find(*key)
56
+ return Statement::Select.new(@model,
57
+ where: @predicate & @model.find_predicate(*key),
58
+ limit: Statement::Limit::ONE
59
+ ).to_a(context)
60
+ end
61
+
62
+ def where(*arguments, **options, &block)
63
+ self.class.new(@context, model,
64
+ @predicate + Statement::Predicate.where(*arguments, **options, &block)
65
+ )
66
+ end
67
+
68
+ def select
69
+ @select ||= Statement::Select.new(@model,
70
+ where: @predicate
71
+ )
72
+ end
73
+
74
+ def each(&block)
75
+ self.select.each(@context, &block)
76
+ end
77
+
78
+ def first(count = nil)
79
+ if count
80
+ self.select.first(@context, count)
81
+ else
82
+ self.select.first(@context, 1).first
83
+ end
84
+ end
85
+
86
+ def to_s
87
+ "\#<#{self.class} #{@model} #{@predicate}>"
88
+ end
89
+
90
+ alias inspect to_s
91
+ end
92
+ end
93
+ end