fluent-query 0.9.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.
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+
3
+ module FluentQuery
4
+ module Compilers
5
+
6
+ ##
7
+ # Query compiler result. Aka compiled string.
8
+ #
9
+
10
+ class Result < ::Array
11
+
12
+ ##
13
+ # Completes the compiled string to final one.
14
+ #
15
+
16
+ def complete(*args)
17
+ result = ""
18
+
19
+ self.each do |v|
20
+ if v.kind_of? Proc
21
+ result << v.call(args.shift)
22
+ else
23
+ result << v.to_s
24
+ end
25
+ end
26
+
27
+ return result
28
+ end
29
+
30
+ end
31
+ end
32
+ end
33
+
@@ -0,0 +1,316 @@
1
+ # encoding: utf-8
2
+ require "fluent-query/result"
3
+ require "fluent-query/driver"
4
+ require "fluent-query/exception"
5
+
6
+ module FluentQuery
7
+
8
+ ##
9
+ # Represents query target connection.
10
+ #
11
+
12
+ class Connection
13
+
14
+ ##
15
+ # Driver instance associated to the connection object.
16
+ #
17
+
18
+ @_driver
19
+
20
+ ##
21
+ # Holds driver class information.
22
+ #
23
+
24
+ @_driver_class
25
+
26
+ ##
27
+ # Connection settings.
28
+ #
29
+
30
+ @_settings
31
+
32
+ ##
33
+ # Indicates, connection is open.
34
+ #
35
+
36
+ @_open
37
+
38
+ ##
39
+ # Indicates debug mode.
40
+ #
41
+
42
+ @_debug
43
+
44
+ ##
45
+ # Initializes the connection.
46
+ #
47
+ # Be warn, initializer call opening the connection immediately on
48
+ # the driver, but if driver is lazy, it will open the connection
49
+ # at the moment in which it will want to do it.
50
+ #
51
+
52
+ public
53
+ def initialize(driver_class, settings = nil, open = true)
54
+
55
+ # Analyses
56
+
57
+ driver = driver_class::new(self)
58
+
59
+ if not driver.kind_of? FluentQuery::Driver
60
+ raise FluentQuery::Exception::new("Driver must be subclass of the 'FluentQuery::Driver' class.")
61
+ end
62
+
63
+ # Assigns
64
+
65
+ @_driver_class = driver_class
66
+ @_settings = settings
67
+ @_open = false
68
+ @_debug = false
69
+
70
+ # Opens if required
71
+
72
+ if open
73
+ @_driver = driver
74
+ self.open!
75
+ end
76
+
77
+ end
78
+
79
+ ##
80
+ # Catches missing methods calls.
81
+ #
82
+ # Asks the driver if method call is relevant for it, of it is,
83
+ # performs it on it.
84
+ #
85
+
86
+ public
87
+ def method_missing(sym, *args, &block)
88
+
89
+ # Checks if connection is in open state
90
+ if not @_open
91
+ raise FluentQuery::Exception::new("Connection is closed.")
92
+ end
93
+
94
+ ##
95
+
96
+ driver = self.driver
97
+
98
+ if driver.relevant_method? sym
99
+ return __query_call(sym, *args, &block)
100
+ else
101
+ raise FluentQuery::Exception::new("Method '" << sym.to_s << "' isn't implemented by associated FluentQuery::Driver or FluentQuery::Connection object.")
102
+ end
103
+ end
104
+
105
+ ##
106
+ # Sets debug mode.
107
+ #
108
+ # Currently two are supported:
109
+ # * false, which turn debugging of
110
+ # * :dump_all which puts all queries to the output
111
+ #
112
+
113
+ public
114
+ def set_debug(mode)
115
+ @_debug = mode
116
+ end
117
+
118
+ ##
119
+ # Generates query object from free query string.
120
+ #
121
+ # But this free query string still will be treated as string only
122
+ # in the final query.
123
+ #
124
+
125
+ public
126
+ def query(*args, &block)
127
+ query = self._new_query_object
128
+
129
+ # Calls given block in query context.
130
+ if block
131
+ result = query.instance_eval(&block)
132
+ else
133
+ result = query
134
+ end
135
+
136
+ return query
137
+ end
138
+
139
+ ##
140
+ # Returns the driver object.
141
+ #
142
+
143
+ public
144
+ def driver
145
+
146
+ # Checks if connection is in open state
147
+ if @_driver.nil?
148
+ @_driver = @_driver_class::new(self)
149
+ end
150
+
151
+ return @_driver
152
+ end
153
+
154
+ ##
155
+ # Opens the connection.
156
+ #
157
+
158
+ public
159
+ def open!(settings = nil)
160
+
161
+ if @_open
162
+ raise FluentQuery::Exception::new("Connection is already open.")
163
+ end
164
+
165
+ ##
166
+
167
+ if (settings == nil) and @_settings
168
+ settings = @_settings
169
+ elsif settings == nil
170
+ raise FluentQuery::Exception::new("Connection settings hasn't been set or given to the #open method.")
171
+ end
172
+
173
+ self.driver.open_connection(settings)
174
+
175
+ ##
176
+
177
+ @_open = true
178
+
179
+ end
180
+
181
+ ##
182
+ # Closes the connection.
183
+ #
184
+
185
+ public
186
+ def close!
187
+ if @_driver
188
+ self.driver.close_connection!
189
+ @_driver = nil
190
+ end
191
+
192
+ @_open = false
193
+ end
194
+
195
+ ##
196
+ # Executes query.
197
+ #
198
+
199
+ public
200
+ def execute(query)
201
+
202
+ # Checks if connection is in open state
203
+ if not @_open
204
+ raise FluentQuery::Exception::new("Connection is closed.")
205
+ end
206
+
207
+ # If query is fluent query object, executes it
208
+ if query.kind_of? FluentQuery::Query
209
+ result = query.execute!
210
+ else
211
+ if @_debug == :dump_all
212
+ puts query
213
+ end
214
+
215
+ result = self.driver.execute(query)
216
+ end
217
+
218
+ # Wraps driver result to result class
219
+ result = FluentQuery::Result::new(result)
220
+
221
+ return result
222
+
223
+ end
224
+
225
+ ##
226
+ # Executes query and returns count of changed/inserted rows.
227
+ #
228
+
229
+ public
230
+ def do(query)
231
+
232
+ # Checks if connection is in open state
233
+ if not @_open
234
+ raise FluentQuery::Exception::new("Connection is closed.")
235
+ end
236
+
237
+ # If query is fluent query object, executes it
238
+ if query.kind_of? FluentQuery::Query
239
+ result = query.do!
240
+ else
241
+ if @_debug == :dump_all
242
+ puts query
243
+ end
244
+
245
+ result = self.driver.do(query)
246
+ end
247
+
248
+ return result
249
+
250
+ end
251
+
252
+ ##
253
+ # Do in transaction context.
254
+ #
255
+
256
+ public
257
+ def transaction(&block)
258
+ self.begin
259
+ block.call
260
+ self.commit
261
+ end
262
+
263
+
264
+
265
+ #####
266
+
267
+ ##
268
+ # Handles built-in shortcut.
269
+ #
270
+
271
+ private
272
+ def __handle_shortcut(sym, *args, &block)
273
+ result = __query_call(sym, *args, &block)
274
+
275
+ if (not args) or (args.length <= 0)
276
+ result = result.execute!
277
+ end
278
+
279
+ return result
280
+ end
281
+
282
+ ##
283
+ # Performs query initiating call.
284
+ #
285
+
286
+ private
287
+ def __query_call(sym, *args, &block)
288
+ query = self._new_query_object
289
+
290
+ # Executes query conditionally. If query isn't suitable for
291
+ # executing, sends the symbol to it and returns call result.
292
+
293
+ query.send(sym, *args)
294
+
295
+ # Calls given block in query context.
296
+
297
+ if block
298
+ result = query.instance_eval(&block)
299
+ else
300
+ result = query
301
+ end
302
+
303
+ return result
304
+ end
305
+
306
+ ##
307
+ # Returns new query object.
308
+ #
309
+
310
+ protected
311
+ def _new_query_object
312
+ self.driver.query_class::new(self)
313
+ end
314
+ end
315
+ end
316
+
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+ require "hash-utils/string"
3
+ require "hashie/mash"
4
+
5
+ module FluentQuery
6
+
7
+ ##
8
+ # Represents data hash.
9
+ #
10
+ # In fact, it's common Hash class extended by method which allow
11
+ # access its fields by "object way".
12
+ #
13
+
14
+ class Data < ::Hashie::Mash
15
+ end
16
+
17
+ end
@@ -0,0 +1,279 @@
1
+ # encoding: utf-8
2
+ require "abstract"
3
+
4
+ module FluentQuery
5
+
6
+ ##
7
+ # Represents abstract query driver.
8
+ # @abstract
9
+ #
10
+
11
+ class Driver
12
+
13
+ ##
14
+ # Holds connection associated to this driver instance.
15
+ #
16
+
17
+ @connection
18
+ attr_reader :connection
19
+
20
+ ##
21
+ # Initializes driver.
22
+ #
23
+
24
+ public
25
+ def initialize(connection)
26
+ if self.instance_of? FluentQuery::Driver
27
+ not_implemented
28
+ end
29
+
30
+ @connection = connection
31
+ end
32
+
33
+ #####
34
+
35
+ ##
36
+ # Indicates, method is relevant for the driver.
37
+ # @abstract
38
+ #
39
+
40
+ public
41
+ def relevant_method?(name)
42
+ not_implemented
43
+ end
44
+
45
+
46
+ ##
47
+ # Indicates token is known.
48
+ # @abstract
49
+ #
50
+
51
+ public
52
+ def known_token?(group, token_name)
53
+ not_implemented
54
+ end
55
+
56
+ ##
57
+ # Indicates, token is operator.
58
+ # @abstract
59
+ #
60
+
61
+ public
62
+ def operator_token?(token_name)
63
+ not_implemented
64
+ end
65
+
66
+ ##
67
+ # Returns operator string according to operator symbol.
68
+ # @abstract
69
+ #
70
+
71
+ public
72
+ def quote_operator(operator)
73
+ not_implemented
74
+ end
75
+
76
+ ##
77
+ # Returns correct equality operator for given datatype.
78
+ #
79
+ # Must handle two modes:
80
+ # * "assigning" which classicaly keeps for example the '=' operator,
81
+ # * "comparing" which sets for example 'IS' operator for booleans.
82
+ #
83
+ # @abstract
84
+ #
85
+
86
+ public
87
+ def quote_equality(datatype, mode = :comparing)
88
+ not_implemented
89
+ end
90
+
91
+ ##
92
+ # Indicates which query subclass to use.
93
+ # @abstract
94
+ #
95
+
96
+ public
97
+ def query_class
98
+ not_implemented
99
+ end
100
+
101
+ ##
102
+ # Builds given query.
103
+ # @abstract
104
+ #
105
+
106
+ public
107
+ def build_query(query)
108
+ not_implemented
109
+ end
110
+
111
+ ##
112
+ # Returns preparation placeholder according to given
113
+ # library placeholder.
114
+ #
115
+ # @abstract
116
+ #
117
+
118
+ public
119
+ def quote_placeholder(placeholder)
120
+ not_implemented
121
+ end
122
+
123
+
124
+ ##### QUOTING
125
+
126
+ ##
127
+ # Quotes string.
128
+ # @abstract
129
+ #
130
+
131
+ public
132
+ def quote_string(string)
133
+ not_implemented
134
+ end
135
+
136
+ ##
137
+ # Quoting integer.
138
+ # @abstract
139
+ #
140
+
141
+ public
142
+ def quote_integer(integer)
143
+ not_implemented
144
+ end
145
+
146
+ ##
147
+ # Quotes float.
148
+ # @abstract
149
+ #
150
+
151
+ public
152
+ def quote_float(float)
153
+ not_implemented
154
+ end
155
+
156
+ ##
157
+ # Quotes field by field quoting.
158
+ # @abstract
159
+ #
160
+
161
+ public
162
+ def quote_identifier(field)
163
+ not_implemented
164
+ end
165
+
166
+ ##
167
+ # Creates system-dependent NULL.
168
+ # @abstract
169
+ #
170
+
171
+ public
172
+ def null
173
+ not_implemented
174
+ end
175
+
176
+ ##
177
+ # Quotes system-dependent boolean value.
178
+ # @abstract
179
+ #
180
+
181
+ public
182
+ def quote_boolean(boolean)
183
+ not_implemented
184
+ end
185
+
186
+ ##
187
+ # Quotes system-dependent date value.
188
+ # @abstract
189
+ #
190
+
191
+ public
192
+ def quote_date_time(date)
193
+ not_implemented
194
+ end
195
+
196
+ ##
197
+ # Quotes system-dependent subquery.
198
+ # @abstract
199
+ #
200
+
201
+ public
202
+ def quote_subquery(subquery)
203
+ not_implemented
204
+ end
205
+
206
+
207
+
208
+ ##### EXECUTING
209
+
210
+ ##
211
+ # Opens the connection.
212
+ #
213
+ # It's lazy, so it will open connection before first request through
214
+ # {@link native_connection()} method.
215
+ #
216
+ # @abstract
217
+ #
218
+
219
+ public
220
+ def open_connection(settings)
221
+ not_implemented
222
+ end
223
+
224
+ ##
225
+ # Returns native connection.
226
+ # @abstract
227
+ #
228
+
229
+ public
230
+ def native_connection
231
+ not_implemented
232
+ end
233
+
234
+ ##
235
+ # Closes the connection.
236
+ # @abstract
237
+ #
238
+
239
+ public
240
+ def close_connection!
241
+ not_implemented
242
+ end
243
+
244
+ ##
245
+ # Executes the query.
246
+ # @abstract
247
+ #
248
+
249
+ public
250
+ def execute(query)
251
+ not_implemented
252
+ end
253
+
254
+ ##
255
+ # Executes the query and returns count of the changed/inserted rows.
256
+ # @abstract
257
+ #
258
+
259
+ public
260
+ def do(query)
261
+ not_implemented
262
+ end
263
+
264
+ ##
265
+ # Generates prepared query. Should be noted, if driver doesn't
266
+ # support query preparing, it should be +not_implemented+ and
267
+ # unimplemented.
268
+ #
269
+ # @abstract
270
+ #
271
+
272
+ public
273
+ def prepare(query)
274
+ not_implemented
275
+ end
276
+
277
+ end
278
+ end
279
+