fluent-query 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +22 -0
- data/Gemfile.lock +22 -0
- data/LICENSE.txt +20 -0
- data/README.md +230 -0
- data/Rakefile +29 -0
- data/VERSION +1 -0
- data/fluent-query.gemspec +75 -0
- data/lib/fluent-query.rb +2 -0
- data/lib/fluent-query/compiler.rb +182 -0
- data/lib/fluent-query/compilers/result.rb +33 -0
- data/lib/fluent-query/connection.rb +316 -0
- data/lib/fluent-query/data.rb +17 -0
- data/lib/fluent-query/driver.rb +279 -0
- data/lib/fluent-query/drivers/exception.rb +13 -0
- data/lib/fluent-query/drivers/result.rb +98 -0
- data/lib/fluent-query/exception.rb +12 -0
- data/lib/fluent-query/queries/abstract.rb +169 -0
- data/lib/fluent-query/queries/compiled.rb +51 -0
- data/lib/fluent-query/queries/prepared.rb +50 -0
- data/lib/fluent-query/queries/processor.rb +392 -0
- data/lib/fluent-query/query.rb +118 -0
- data/lib/fluent-query/result.rb +153 -0
- data/lib/fluent-query/token.rb +73 -0
- data/lib/fluent-query/tokens/raw.rb +26 -0
- metadata +136 -0
@@ -0,0 +1,98 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "abstract"
|
3
|
+
|
4
|
+
module FluentQuery
|
5
|
+
module Drivers
|
6
|
+
|
7
|
+
##
|
8
|
+
# Represents general driver result accessor.
|
9
|
+
#
|
10
|
+
|
11
|
+
class Result
|
12
|
+
|
13
|
+
##
|
14
|
+
# Initializes result.
|
15
|
+
#
|
16
|
+
|
17
|
+
public
|
18
|
+
def initialize
|
19
|
+
if self.instance_of? FluentQuery::Drivers::Result
|
20
|
+
not_implemented
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Returns all selected rows.
|
26
|
+
#
|
27
|
+
|
28
|
+
public
|
29
|
+
def all
|
30
|
+
not_implemented
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Returns one row.
|
35
|
+
#
|
36
|
+
|
37
|
+
public
|
38
|
+
def one
|
39
|
+
not_implemented
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Returns first value of first row.
|
44
|
+
#
|
45
|
+
|
46
|
+
public
|
47
|
+
def single
|
48
|
+
not_implemented
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Returns row as hash.
|
53
|
+
#
|
54
|
+
|
55
|
+
public
|
56
|
+
def hash
|
57
|
+
not_implemented
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Handles iterating.
|
62
|
+
#
|
63
|
+
|
64
|
+
public
|
65
|
+
def each
|
66
|
+
not_implemented
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
##
|
71
|
+
# Repeats the query leaded to the result.
|
72
|
+
#
|
73
|
+
|
74
|
+
public
|
75
|
+
def repeat!
|
76
|
+
not_implemented
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# Returns rows count.
|
81
|
+
#
|
82
|
+
|
83
|
+
public
|
84
|
+
def count
|
85
|
+
not_implemented
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Frees result resources.
|
90
|
+
#
|
91
|
+
|
92
|
+
public
|
93
|
+
def free!
|
94
|
+
not_implemented
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "fluent-query/queries/processor"
|
3
|
+
require "abstract"
|
4
|
+
|
5
|
+
module FluentQuery
|
6
|
+
module Queries
|
7
|
+
|
8
|
+
##
|
9
|
+
# Represents and abstract query.
|
10
|
+
#
|
11
|
+
|
12
|
+
class Abstract
|
13
|
+
|
14
|
+
##
|
15
|
+
# Holds connection object.
|
16
|
+
#
|
17
|
+
|
18
|
+
@connection
|
19
|
+
attr_reader :connection
|
20
|
+
|
21
|
+
##
|
22
|
+
# Holds appropriate query processor.
|
23
|
+
#
|
24
|
+
|
25
|
+
@processor
|
26
|
+
|
27
|
+
##
|
28
|
+
# Constructor.
|
29
|
+
#
|
30
|
+
|
31
|
+
public
|
32
|
+
def initialize(connection)
|
33
|
+
@connection = connection
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# Returns processor.
|
38
|
+
#
|
39
|
+
|
40
|
+
public
|
41
|
+
def processor
|
42
|
+
if not @processor
|
43
|
+
driver = @connection.driver
|
44
|
+
@processor = FluentQuery::Queries::Processor::new(driver)
|
45
|
+
end
|
46
|
+
|
47
|
+
return @processor
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Builds prepared query string to final form.
|
52
|
+
#
|
53
|
+
|
54
|
+
public
|
55
|
+
def build(*args)
|
56
|
+
not_implemented
|
57
|
+
end
|
58
|
+
|
59
|
+
alias :"build!" :build
|
60
|
+
|
61
|
+
##
|
62
|
+
# Executes query and returns result object.
|
63
|
+
#
|
64
|
+
|
65
|
+
public
|
66
|
+
def execute(*args)
|
67
|
+
@connection.execute(self.build(*args))
|
68
|
+
end
|
69
|
+
|
70
|
+
alias :"execute!" :execute
|
71
|
+
|
72
|
+
##
|
73
|
+
# Executes query and returns count of changed/inserted rows.
|
74
|
+
#
|
75
|
+
|
76
|
+
public
|
77
|
+
def do(*args)
|
78
|
+
@connection.do(self.build(*args))
|
79
|
+
end
|
80
|
+
|
81
|
+
alias :"do!" :do
|
82
|
+
|
83
|
+
|
84
|
+
##
|
85
|
+
# Returns all selected rows.
|
86
|
+
#
|
87
|
+
|
88
|
+
public
|
89
|
+
def all(*args)
|
90
|
+
self.execute(*args).all
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
##
|
95
|
+
# Returns one row.
|
96
|
+
#
|
97
|
+
|
98
|
+
public
|
99
|
+
def one(*args)
|
100
|
+
self.execute(*args).one
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# Returns all selected rows ordered according to datafield from it.
|
105
|
+
#
|
106
|
+
|
107
|
+
public
|
108
|
+
def assoc(*specification, &block)
|
109
|
+
self.execute.assoc(*specification, &block)
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Returns first value of first row.
|
114
|
+
#
|
115
|
+
|
116
|
+
public
|
117
|
+
def single(*args)
|
118
|
+
self.execute(*args).single
|
119
|
+
end
|
120
|
+
|
121
|
+
##
|
122
|
+
# Handles iterating.
|
123
|
+
#
|
124
|
+
|
125
|
+
public
|
126
|
+
def each(*args)
|
127
|
+
self.execute(*args).each do |item|
|
128
|
+
yield item
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
##
|
133
|
+
# Maps callback to array.
|
134
|
+
#
|
135
|
+
|
136
|
+
public
|
137
|
+
def map(*args, &block)
|
138
|
+
result = [ ]
|
139
|
+
|
140
|
+
self.each(*args) do |item|
|
141
|
+
result << block.call(item)
|
142
|
+
end
|
143
|
+
|
144
|
+
return result
|
145
|
+
end
|
146
|
+
|
147
|
+
##
|
148
|
+
# Returns only rows for which block is true.
|
149
|
+
#
|
150
|
+
|
151
|
+
public
|
152
|
+
def find_all(*args, &block)
|
153
|
+
result = [ ]
|
154
|
+
|
155
|
+
self.each(*args) do |item|
|
156
|
+
if block.call(item) === true
|
157
|
+
result << block.call(item)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
return result
|
162
|
+
end
|
163
|
+
|
164
|
+
alias :filter :find_all
|
165
|
+
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "fluent-query/queries/abstract"
|
3
|
+
|
4
|
+
module FluentQuery
|
5
|
+
module Queries
|
6
|
+
|
7
|
+
##
|
8
|
+
# Compiled query.
|
9
|
+
#
|
10
|
+
|
11
|
+
class Compiled < FluentQuery::Queries::Abstract
|
12
|
+
|
13
|
+
##
|
14
|
+
# Holds query in compiled form.
|
15
|
+
#
|
16
|
+
|
17
|
+
@raw
|
18
|
+
attr_accessor :raw
|
19
|
+
|
20
|
+
##
|
21
|
+
# Constructor.
|
22
|
+
#
|
23
|
+
|
24
|
+
public
|
25
|
+
def initialize(connection, query)
|
26
|
+
super(connection)
|
27
|
+
@raw = query.processor.compile(@connection.driver.build_query(query, :compile))
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Builds prepared query string to final form.
|
32
|
+
#
|
33
|
+
|
34
|
+
public
|
35
|
+
def build(*args)
|
36
|
+
@raw.complete(*args)
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Returns all selected rows ordered according to datafield from it.
|
41
|
+
#
|
42
|
+
|
43
|
+
public
|
44
|
+
def assoc(specification, *args)
|
45
|
+
self.execute(*args).assoc(specification)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "fluent-query/queries/abstract"
|
3
|
+
|
4
|
+
module FluentQuery
|
5
|
+
module Queries
|
6
|
+
|
7
|
+
##
|
8
|
+
# Compiled query.
|
9
|
+
#
|
10
|
+
|
11
|
+
class Prepared < FluentQuery::Queries::Abstract
|
12
|
+
|
13
|
+
##
|
14
|
+
# Holds query in prepared form.
|
15
|
+
#
|
16
|
+
|
17
|
+
@query
|
18
|
+
|
19
|
+
##
|
20
|
+
# Constructor.
|
21
|
+
#
|
22
|
+
|
23
|
+
public
|
24
|
+
def initialize(connection, query)
|
25
|
+
super(connection)
|
26
|
+
@query = @connection.driver.prepare(query)
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Builds prepared query string to final form.
|
31
|
+
#
|
32
|
+
|
33
|
+
public
|
34
|
+
def build(*args)
|
35
|
+
[@query, args]
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Returns all selected rows ordered according to datafield from it.
|
40
|
+
#
|
41
|
+
|
42
|
+
public
|
43
|
+
def assoc(specification, *args)
|
44
|
+
self.execute(*args).assoc(specification)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,392 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "date"
|
3
|
+
require "hash-utils/string"
|
4
|
+
|
5
|
+
require "fluent-query/query"
|
6
|
+
require "fluent-query/compiler"
|
7
|
+
|
8
|
+
module FluentQuery
|
9
|
+
module Queries
|
10
|
+
|
11
|
+
##
|
12
|
+
# Query processor.
|
13
|
+
#
|
14
|
+
# Its primary aim is to dive processing methods from query object as is.
|
15
|
+
# In fact, defines something as processing session.
|
16
|
+
#
|
17
|
+
|
18
|
+
class Processor
|
19
|
+
|
20
|
+
##
|
21
|
+
# Indicates column replacement to perform.
|
22
|
+
# (Flag.)
|
23
|
+
#
|
24
|
+
|
25
|
+
COLUMN_REPLACEMENT = 2
|
26
|
+
|
27
|
+
##
|
28
|
+
# Indicates string replacement to perform.
|
29
|
+
# (Flag.)
|
30
|
+
#
|
31
|
+
|
32
|
+
STRING_REPLACEMENT = 1
|
33
|
+
|
34
|
+
##
|
35
|
+
# Indicates formatting directive replacements to perform.
|
36
|
+
# (Flag.)
|
37
|
+
#
|
38
|
+
|
39
|
+
FORMATTING_REPLACEMENT = 4
|
40
|
+
|
41
|
+
##
|
42
|
+
# Describes formatting directive.
|
43
|
+
# @var Regexp
|
44
|
+
#
|
45
|
+
|
46
|
+
FORMATTING_DIRECTIVE = /%%(?:[islbfdt]|sql|and|or)(?:[^\w]|$)/
|
47
|
+
|
48
|
+
##
|
49
|
+
# Describes column directive.
|
50
|
+
# @var Regexp
|
51
|
+
#
|
52
|
+
|
53
|
+
COLUMN_DIRECTIVE = /(?:^|\.|[^\'"\w\\])?\[(?:[\w\.]*[^\\\W])\](?:[^\'"\w]|\.|$)/ # problem is, it interpretes regex
|
54
|
+
# from left to right, so if second
|
55
|
+
# occurence is immedieately
|
56
|
+
# it will not be matched -- that
|
57
|
+
# is reason for ? in the begin section
|
58
|
+
|
59
|
+
##
|
60
|
+
# Describes string directive.
|
61
|
+
# @var Regexp
|
62
|
+
#
|
63
|
+
|
64
|
+
STRING_DIRECTIVE = /(?:^|[^\\])"/
|
65
|
+
|
66
|
+
##
|
67
|
+
# Holds simple column definition.
|
68
|
+
# @var Regexp
|
69
|
+
#
|
70
|
+
|
71
|
+
FORMATTING_DIRECTIVE_SIMPLE = /%%\w+/
|
72
|
+
|
73
|
+
##
|
74
|
+
# Holds simple column definition.
|
75
|
+
#
|
76
|
+
|
77
|
+
COLUMN_DIRECTIVE_SIMPLE = /\[.+\]/
|
78
|
+
|
79
|
+
##
|
80
|
+
# Final regexp for incremental fluent expanding.
|
81
|
+
# (internal cache)
|
82
|
+
|
83
|
+
@__fluent_expander
|
84
|
+
|
85
|
+
##
|
86
|
+
# Final regexp for incremental fluent replacing.
|
87
|
+
# (internal cache)
|
88
|
+
|
89
|
+
@__fluent_replacer
|
90
|
+
|
91
|
+
##
|
92
|
+
# Driver upon which processor should work upon.
|
93
|
+
#
|
94
|
+
|
95
|
+
@driver
|
96
|
+
attr_reader :driver
|
97
|
+
|
98
|
+
##
|
99
|
+
# Compiler associated to this processor.
|
100
|
+
#
|
101
|
+
|
102
|
+
@_compiler
|
103
|
+
|
104
|
+
##
|
105
|
+
# Constructs processor.
|
106
|
+
# @param MP_Fluent_Driver driver Driver upon which processor should work upon.
|
107
|
+
#
|
108
|
+
|
109
|
+
public
|
110
|
+
def initialize(driver)
|
111
|
+
@driver = driver
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Process array to some reasonable string form.
|
116
|
+
#
|
117
|
+
# Joins its elements and separates them by commas. Quotes strings
|
118
|
+
# by by-driver-defined string quoting and integers by integer quoting.
|
119
|
+
#
|
120
|
+
|
121
|
+
public
|
122
|
+
def process_array(array, glue = ",")
|
123
|
+
result = [ ]
|
124
|
+
|
125
|
+
array.each do |item|
|
126
|
+
result << self.quote_value(item)
|
127
|
+
end
|
128
|
+
|
129
|
+
return result.join(glue + " ")
|
130
|
+
end
|
131
|
+
|
132
|
+
##
|
133
|
+
# Process hash to some reasonable string form.
|
134
|
+
#
|
135
|
+
# Joins its elements to name = value form and separates them
|
136
|
+
# by commas. Quotes string value by by-driver-defined string quoting
|
137
|
+
# and integers by integer quoting. Quotes name by identifiers quoting.
|
138
|
+
#
|
139
|
+
# Handles two modes:
|
140
|
+
# * "assigning" which classicaly keeps for example the '=' operator,
|
141
|
+
# * "comparing" which sets for example 'IS' operator for booleans.
|
142
|
+
#
|
143
|
+
|
144
|
+
public
|
145
|
+
def process_hash(hash, glue = ",", equality = :comparing)
|
146
|
+
result = [ ]
|
147
|
+
mode = equality
|
148
|
+
|
149
|
+
hash.each_pair do |key, value|
|
150
|
+
operator = @driver.quote_equality(value, mode)
|
151
|
+
key = self.quote_identifier(key)
|
152
|
+
value = self.quote_value(value)
|
153
|
+
|
154
|
+
result << (key + " " + operator + " " + value)
|
155
|
+
end
|
156
|
+
|
157
|
+
return result.join(" " << glue << " ")
|
158
|
+
end
|
159
|
+
|
160
|
+
##
|
161
|
+
# Quotes string by driver string quoting and integers by integer
|
162
|
+
# quoting. Other values are converted to string too and
|
163
|
+
# leaved unquoted.
|
164
|
+
#
|
165
|
+
|
166
|
+
public
|
167
|
+
def quote_value(value)
|
168
|
+
|
169
|
+
if (value.string?) or (value.symbol?)
|
170
|
+
result = @driver.quote_string(value.to_s)
|
171
|
+
elsif value.kind_of? Integer
|
172
|
+
result = @driver.quote_integer(value)
|
173
|
+
elsif value.array?
|
174
|
+
result = "(" << self.process_array(value) << ")" # TODO: question is, if we should do it here, if it's enough general just for processor
|
175
|
+
elsif value.kind_of? Float
|
176
|
+
result = @driver.quote_float(value)
|
177
|
+
elsif (value.kind_of? TrueClass) or (value.kind_of? FalseClass)
|
178
|
+
result = @driver.quote_boolean(value)
|
179
|
+
elsif (value.kind_of? Date) or (value.kind_of? DateTime)
|
180
|
+
result = @driver.quote_date_time(value)
|
181
|
+
elsif value.nil?
|
182
|
+
result = @driver.null
|
183
|
+
else
|
184
|
+
result = value.to_s
|
185
|
+
end
|
186
|
+
|
187
|
+
return result
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
##
|
192
|
+
# Quotes subquery by driver subquery quoting.
|
193
|
+
#
|
194
|
+
|
195
|
+
public
|
196
|
+
def quote_subquery(subquery)
|
197
|
+
if subquery.kind_of? FluentQuery::Query
|
198
|
+
subquery = subquery.build!
|
199
|
+
end
|
200
|
+
|
201
|
+
return @driver.quote_subquery(subquery)
|
202
|
+
end
|
203
|
+
|
204
|
+
##
|
205
|
+
# Quotes identifiers by identifier quoting.
|
206
|
+
#
|
207
|
+
|
208
|
+
public
|
209
|
+
def quote_identifier(identifier)
|
210
|
+
@driver.quote_identifier(identifier.to_s)
|
211
|
+
end
|
212
|
+
|
213
|
+
##
|
214
|
+
# Quotes identifiers list.
|
215
|
+
#
|
216
|
+
|
217
|
+
public
|
218
|
+
def quote_identifiers(identifiers)
|
219
|
+
identifiers.map { |i| self.quote_identifier(i) }
|
220
|
+
end
|
221
|
+
|
222
|
+
##
|
223
|
+
# Processes identifier list to the string representation.
|
224
|
+
# Quotes them and joins them separated by commas.
|
225
|
+
#
|
226
|
+
|
227
|
+
public
|
228
|
+
def process_identifiers(identifiers)
|
229
|
+
self.quote_identifiers(identifiers).join(", ")
|
230
|
+
end
|
231
|
+
|
232
|
+
##
|
233
|
+
# Processed strings with format definitions and data specifications.
|
234
|
+
#
|
235
|
+
# Mode can be :compile, :build or :finish. Compiling means building the
|
236
|
+
# query without expanding the formatting directives. Finishing means
|
237
|
+
# building the prepared query.
|
238
|
+
#
|
239
|
+
|
240
|
+
public
|
241
|
+
def process_formatted(sequence, mode = :build)
|
242
|
+
count = sequence.length
|
243
|
+
i = 0
|
244
|
+
output = ""
|
245
|
+
|
246
|
+
replacer_settings = self.class::COLUMN_REPLACEMENT | self.class::STRING_REPLACEMENT
|
247
|
+
expander_settings = self.class::FORMATTING_REPLACEMENT
|
248
|
+
|
249
|
+
while i < count
|
250
|
+
item = sequence[i]
|
251
|
+
|
252
|
+
##
|
253
|
+
|
254
|
+
if item.kind_of? String
|
255
|
+
item = item.dup
|
256
|
+
|
257
|
+
# Calls to each occurence of directive matching to directive
|
258
|
+
# finding expression directive processing. In each call increases
|
259
|
+
# sequence position counter so moves forward.
|
260
|
+
|
261
|
+
if mode != :finish
|
262
|
+
item.gsub!(self.replacer) do |directive|
|
263
|
+
directive.strip!
|
264
|
+
self.process_directive(directive, nil, replacer_settings)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
if mode != :compile
|
269
|
+
item.gsub!(self.expander) do |directive|
|
270
|
+
self.process_directive(directive, sequence[i += 1], expander_settings)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
output << item
|
275
|
+
|
276
|
+
elsif item.kind_of? Symbol
|
277
|
+
output << self.quote_identifier(item)
|
278
|
+
|
279
|
+
else
|
280
|
+
output << item.to_s
|
281
|
+
|
282
|
+
end
|
283
|
+
|
284
|
+
output << " "
|
285
|
+
i += 1
|
286
|
+
end
|
287
|
+
|
288
|
+
return output
|
289
|
+
|
290
|
+
end
|
291
|
+
|
292
|
+
##
|
293
|
+
# Processes fluent directive.
|
294
|
+
# Replacements is three bytes flag array.
|
295
|
+
#
|
296
|
+
|
297
|
+
public
|
298
|
+
def process_directive(directive, argument = nil, replacements = 7)
|
299
|
+
if (replacements & self.class::COLUMN_REPLACEMENT > 0) and (directive[0].ord == 91) # "[", Column directive
|
300
|
+
result = directive.gsub(self.class::COLUMN_DIRECTIVE_SIMPLE) { |value| self.quote_identifier(value[1..-2]) }
|
301
|
+
elsif (replacements & self.class::STRING_REPLACEMENT > 0) and (directive[-1].ord == 34) # "\"", String directive
|
302
|
+
result = directive.gsub('"', @driver.quote_string("").last)
|
303
|
+
elsif (replacements & self.class::FORMATTING_REPLACEMENT > 0) and (directive[0..1].to_sym == :"%%") # Formatting directive
|
304
|
+
result = directive.gsub(self.class::FORMATTING_DIRECTIVE_SIMPLE) { |value| self.process_formatting(value[2..-1], argument) }
|
305
|
+
else
|
306
|
+
result = directive
|
307
|
+
end
|
308
|
+
|
309
|
+
return result
|
310
|
+
end
|
311
|
+
|
312
|
+
##
|
313
|
+
# Processes formatting directive.
|
314
|
+
#
|
315
|
+
|
316
|
+
public
|
317
|
+
def process_formatting(directive, argument)
|
318
|
+
proc = self.compiler.compile_formatting(directive)
|
319
|
+
|
320
|
+
if proc
|
321
|
+
output = proc.call(argument)
|
322
|
+
else
|
323
|
+
output = ""
|
324
|
+
end
|
325
|
+
|
326
|
+
return output
|
327
|
+
end
|
328
|
+
|
329
|
+
##
|
330
|
+
# Returns NULL representation.
|
331
|
+
#
|
332
|
+
|
333
|
+
public
|
334
|
+
def null
|
335
|
+
@driver.null
|
336
|
+
end
|
337
|
+
|
338
|
+
##
|
339
|
+
# Returns final fluent expander query.
|
340
|
+
#
|
341
|
+
|
342
|
+
public
|
343
|
+
def expander
|
344
|
+
if @__fluent_expander.nil?
|
345
|
+
@__fluent_expander = self.class::FORMATTING_DIRECTIVE
|
346
|
+
end
|
347
|
+
|
348
|
+
@__fluent_expander
|
349
|
+
end
|
350
|
+
|
351
|
+
##
|
352
|
+
# Returns final fluent formatter query.
|
353
|
+
#
|
354
|
+
|
355
|
+
public
|
356
|
+
def replacer
|
357
|
+
if @__fluent_replacer.nil?
|
358
|
+
parts = Array[self.class::COLUMN_DIRECTIVE, self.class::STRING_DIRECTIVE]
|
359
|
+
|
360
|
+
result = ""
|
361
|
+
result << "(?:(?:" << parts.join(")|(?:") << "))"
|
362
|
+
|
363
|
+
@__fluent_replacer = Regexp::new(result)
|
364
|
+
end
|
365
|
+
|
366
|
+
@__fluent_replacer
|
367
|
+
end
|
368
|
+
|
369
|
+
##
|
370
|
+
# Returns compiler.
|
371
|
+
#
|
372
|
+
|
373
|
+
def compiler
|
374
|
+
if @_compiler.nil?
|
375
|
+
@_compiler = FluentQuery::Compiler::new(self)
|
376
|
+
end
|
377
|
+
|
378
|
+
@_compiler
|
379
|
+
end
|
380
|
+
|
381
|
+
##
|
382
|
+
# Compiles the string.
|
383
|
+
#
|
384
|
+
|
385
|
+
def compile(string)
|
386
|
+
self.compiler.compile(string)
|
387
|
+
end
|
388
|
+
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|