metry 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. data/History.txt +6 -0
  2. data/Manifest.txt +14 -69
  3. data/TODO +2 -3
  4. data/cucumber.yml +1 -0
  5. data/example/example.rb +1 -1
  6. data/features/psycho/dashboard.feature +17 -0
  7. data/features/psycho/goals.feature +24 -0
  8. data/features/psycho/visitor_tracking.feature +32 -0
  9. data/features/step_definitions/goals.rb +3 -0
  10. data/features/step_definitions/tracking.rb +1 -1
  11. data/features/step_definitions/web.rb +14 -2
  12. data/features/support/env.rb +4 -0
  13. data/lib/metry.rb +4 -6
  14. data/lib/metry/experiment.rb +6 -8
  15. data/lib/metry/psycho.rb +112 -0
  16. data/lib/metry/psycho/dashboard.erb +14 -0
  17. data/lib/metry/psycho/layout.erb +8 -0
  18. data/lib/metry/psycho/new_goal.erb +9 -0
  19. data/lib/metry/psycho/visitor.erb +7 -0
  20. data/lib/metry/rack/tracking.rb +17 -13
  21. data/lib/metry/storage.rb +116 -0
  22. data/radiant/example/features/metry.feature +8 -8
  23. data/radiant/example/features/psycho.feature +13 -0
  24. data/radiant/example/features/step_definitions/experiments.rb +2 -2
  25. data/radiant/example/features/step_definitions/tracking.rb +1 -1
  26. data/radiant/example/features/support/env.rb +3 -0
  27. data/radiant/extension/lib/metry_authenticator.rb +30 -0
  28. data/radiant/extension/lib/metry_tags.rb +2 -1
  29. data/radiant/extension/metry_extension.rb +1 -1
  30. data/test/{test_tokyo.rb → test_storage.rb} +3 -3
  31. metadata +17 -72
  32. data/lib/metry/memory.rb +0 -27
  33. data/lib/metry/tokyo.rb +0 -181
  34. data/vendor/rufus-tokyo/CHANGELOG.txt +0 -112
  35. data/vendor/rufus-tokyo/CREDITS.txt +0 -27
  36. data/vendor/rufus-tokyo/LICENSE.txt +0 -21
  37. data/vendor/rufus-tokyo/README.txt +0 -310
  38. data/vendor/rufus-tokyo/Rakefile +0 -118
  39. data/vendor/rufus-tokyo/TODO.txt +0 -25
  40. data/vendor/rufus-tokyo/doc/decision_table.numbers +0 -0
  41. data/vendor/rufus-tokyo/doc/rdoc-style.css +0 -320
  42. data/vendor/rufus-tokyo/lib/rufus-edo.rb +0 -3
  43. data/vendor/rufus-tokyo/lib/rufus-tokyo.rb +0 -3
  44. data/vendor/rufus-tokyo/lib/rufus/edo.rb +0 -39
  45. data/vendor/rufus-tokyo/lib/rufus/edo/README.txt +0 -106
  46. data/vendor/rufus-tokyo/lib/rufus/edo/cabcore.rb +0 -333
  47. data/vendor/rufus-tokyo/lib/rufus/edo/cabinet/abstract.rb +0 -219
  48. data/vendor/rufus-tokyo/lib/rufus/edo/cabinet/table.rb +0 -159
  49. data/vendor/rufus-tokyo/lib/rufus/edo/error.rb +0 -36
  50. data/vendor/rufus-tokyo/lib/rufus/edo/ntyrant.rb +0 -4
  51. data/vendor/rufus-tokyo/lib/rufus/edo/ntyrant/abstract.rb +0 -137
  52. data/vendor/rufus-tokyo/lib/rufus/edo/ntyrant/table.rb +0 -141
  53. data/vendor/rufus-tokyo/lib/rufus/edo/tabcore.rb +0 -567
  54. data/vendor/rufus-tokyo/lib/rufus/tokyo.rb +0 -58
  55. data/vendor/rufus-tokyo/lib/rufus/tokyo/cabinet/abstract.rb +0 -568
  56. data/vendor/rufus-tokyo/lib/rufus/tokyo/cabinet/lib.rb +0 -230
  57. data/vendor/rufus-tokyo/lib/rufus/tokyo/cabinet/table.rb +0 -753
  58. data/vendor/rufus-tokyo/lib/rufus/tokyo/cabinet/util.rb +0 -425
  59. data/vendor/rufus-tokyo/lib/rufus/tokyo/config.rb +0 -161
  60. data/vendor/rufus-tokyo/lib/rufus/tokyo/dystopia.rb +0 -43
  61. data/vendor/rufus-tokyo/lib/rufus/tokyo/dystopia/lib.rb +0 -65
  62. data/vendor/rufus-tokyo/lib/rufus/tokyo/dystopia/words.rb +0 -71
  63. data/vendor/rufus-tokyo/lib/rufus/tokyo/hmethods.rb +0 -111
  64. data/vendor/rufus-tokyo/lib/rufus/tokyo/query.rb +0 -102
  65. data/vendor/rufus-tokyo/lib/rufus/tokyo/transactions.rb +0 -74
  66. data/vendor/rufus-tokyo/lib/rufus/tokyo/ttcommons.rb +0 -59
  67. data/vendor/rufus-tokyo/lib/rufus/tokyo/tyrant.rb +0 -35
  68. data/vendor/rufus-tokyo/lib/rufus/tokyo/tyrant/abstract.rb +0 -146
  69. data/vendor/rufus-tokyo/lib/rufus/tokyo/tyrant/lib.rb +0 -153
  70. data/vendor/rufus-tokyo/lib/rufus/tokyo/tyrant/table.rb +0 -162
  71. data/vendor/rufus-tokyo/rufus-tokyo.gemspec +0 -25
  72. data/vendor/rufus-tokyo/spec/cabinet_spec.rb +0 -472
  73. data/vendor/rufus-tokyo/spec/cabinetconfig_spec.rb +0 -82
  74. data/vendor/rufus-tokyo/spec/edo_cabinet_spec.rb +0 -447
  75. data/vendor/rufus-tokyo/spec/edo_ntyrant_spec.rb +0 -299
  76. data/vendor/rufus-tokyo/spec/edo_ntyrant_table_spec.rb +0 -462
  77. data/vendor/rufus-tokyo/spec/edo_table_spec.rb +0 -560
  78. data/vendor/rufus-tokyo/spec/hmethods_spec.rb +0 -44
  79. data/vendor/rufus-tokyo/spec/incr.lua +0 -20
  80. data/vendor/rufus-tokyo/spec/spec.rb +0 -9
  81. data/vendor/rufus-tokyo/spec/spec_base.rb +0 -23
  82. data/vendor/rufus-tokyo/spec/start_tyrants.sh +0 -26
  83. data/vendor/rufus-tokyo/spec/stop_tyrants.sh +0 -9
  84. data/vendor/rufus-tokyo/spec/table_spec.rb +0 -567
  85. data/vendor/rufus-tokyo/spec/tyrant_spec.rb +0 -309
  86. data/vendor/rufus-tokyo/spec/tyrant_table_spec.rb +0 -479
  87. data/vendor/rufus-tokyo/spec/util_list_spec.rb +0 -200
  88. data/vendor/rufus-tokyo/spec/util_map_spec.rb +0 -132
  89. data/vendor/rufus-tokyo/tasks/dev.rb +0 -70
  90. data/vendor/rufus-tokyo/test/bm0.rb +0 -353
  91. data/vendor/rufus-tokyo/test/bm1_compression.rb +0 -54
  92. data/vendor/rufus-tokyo/test/con0.rb +0 -30
  93. data/vendor/rufus-tokyo/test/mem.rb +0 -49
  94. data/vendor/rufus-tokyo/test/mem1.rb +0 -44
  95. data/vendor/rufus-tokyo/test/readme0.rb +0 -17
  96. data/vendor/rufus-tokyo/test/readme1.rb +0 -21
  97. data/vendor/rufus-tokyo/test/readme2.rb +0 -15
  98. data/vendor/rufus-tokyo/test/readme3.rb +0 -24
  99. data/vendor/rufus-tokyo/test/readmes_test.sh +0 -17
@@ -1,141 +0,0 @@
1
- #--
2
- # Copyright (c) 2009, John Mettraux, jmettraux@gmail.com
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining a copy
5
- # of this software and associated documentation files (the "Software"), to deal
6
- # in the Software without restriction, including without limitation the rights
7
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
- # copies of the Software, and to permit persons to whom the Software is
9
- # furnished to do so, subject to the following conditions:
10
- #
11
- # The above copyright notice and this permission notice shall be included in
12
- # all copies or substantial portions of the Software.
13
- #
14
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
- # THE SOFTWARE.
21
- #
22
- # Made in Japan.
23
- #++
24
-
25
-
26
- require 'tokyotyrant'
27
-
28
- require 'rufus/edo/error'
29
- require 'rufus/edo/tabcore'
30
- require 'rufus/tokyo/ttcommons'
31
-
32
-
33
- module Rufus::Edo
34
-
35
- #
36
- # A Tokyo Cabinet table, but remote...
37
- #
38
- # require 'rufus/edo/ntyrant'
39
- # t = Rufus::Edo::NetTyrant.new('127.0.0.1', 44001)
40
- # t['toto'] = { 'name' => 'toto the first', 'age' => '34' }
41
- # t['toto']
42
- # # => { 'name' => 'toto the first', 'age' => '34' }
43
- #
44
- # NOTE : The advantage of this class is that it leverages the TokyoTyrant.rb
45
- # provided by Hirabayashi-san. It's pure Ruby, it's slow but works everywhere
46
- # without the need for Tokyo Cabinet and Tyrant C libraries.
47
- #
48
- class NetTyrantTable
49
-
50
- include Rufus::Edo::TableCore
51
- include Rufus::Tokyo::TyrantCommons
52
-
53
- attr_reader :host, :port
54
-
55
- #
56
- # Connects to the Tyrant table listening at the given host and port.
57
- #
58
- # You start such a Tyrant with :
59
- #
60
- # ttserver -port 44502 data.tct
61
- #
62
- # and then :
63
- #
64
- # require 'rufus/edo/ntyrant'
65
- # t = Rufus::Edo::NetTyrantTable.new('127.0.0.1', 44502)
66
- # t['client0'] = { 'name' => 'Heike no Kyomori', 'country' => 'jp' }
67
- # t.close
68
- #
69
- #
70
- # You can start a Tokyo Tyrant and make it listen to a unix socket (not TCP)
71
- # with :
72
- #
73
- # ttserver -host /tmp/table_socket -port 0 data.tct
74
- #
75
- # then :
76
- #
77
- # require 'rufus/edo/ntyrant'
78
- # t = Rufus::Edo::NetTyrantTable.new('/tmp/table_socket')
79
- # t['client0'] = { 'name' => 'Theodore Roosevelt', 'country' => 'usa' }
80
- # t.close
81
- #
82
- def initialize (host, port=0)
83
-
84
- @host = host
85
- @port = port
86
-
87
- @db = TokyoTyrant::RDBTBL.new
88
- @db.open(host, port) || raise_error
89
-
90
- if self.stat['type'] != 'table'
91
-
92
- @db.close
93
-
94
- raise ArgumentError.new(
95
- "tyrant at #{host}:#{port} is not a table, " +
96
- "use Rufus::Edo::NetTyrant instead to access it.")
97
- end
98
- end
99
-
100
- # Gets multiple records in one sweep.
101
- #
102
- def lget (keys)
103
-
104
- h = keys.inject({}) { |h, k| h[k] = nil; h }
105
- r = @db.mget(h)
106
-
107
- raise 'lget failure' if r == -1
108
-
109
- h
110
- end
111
-
112
- def transaction #:nodoc#
113
- raise NoMethodError.new("NetTyrant : transactions not supported")
114
- end
115
- def abort #:nodoc#
116
- raise NoMethodError.new("NetTyrant : transactions not supported")
117
- end
118
- def tranbegin #:nodoc#
119
- raise NoMethodError.new("NetTyrant : transactions not supported")
120
- end
121
- def trancommit #:nodoc#
122
- raise NoMethodError.new("NetTyrant : transactions not supported")
123
- end
124
- def tranabort #:nodoc#
125
- raise NoMethodError.new("NetTyrant : transactions not supported")
126
- end
127
-
128
- protected
129
-
130
- def table_query_class #:nodoc#
131
-
132
- TokyoTyrant::RDBQRY
133
- end
134
-
135
- def do_stat #:nodoc#
136
-
137
- @db.stat
138
- end
139
- end
140
- end
141
-
@@ -1,567 +0,0 @@
1
- #--
2
- # Copyright (c) 2009, John Mettraux, jmettraux@gmail.com
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining a copy
5
- # of this software and associated documentation files (the "Software"), to deal
6
- # in the Software without restriction, including without limitation the rights
7
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
- # copies of the Software, and to permit persons to whom the Software is
9
- # furnished to do so, subject to the following conditions:
10
- #
11
- # The above copyright notice and this permission notice shall be included in
12
- # all copies or substantial portions of the Software.
13
- #
14
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
- # THE SOFTWARE.
21
- #
22
- # Made in Japan.
23
- #++
24
-
25
-
26
- require 'rufus/tokyo/query'
27
- require 'rufus/tokyo/transactions'
28
-
29
-
30
- module Rufus::Edo
31
-
32
- #
33
- # Methods common to the two table classes (cabinet + ntyrant) found in
34
- # Rufus::Edo
35
- #
36
- module TableCore
37
-
38
- include Rufus::Tokyo::HashMethods
39
- include Rufus::Tokyo::Transactions
40
-
41
- # Closes the table (and frees the datastructure allocated for it),
42
- # raises an exception in case of failure.
43
- #
44
- def close
45
- @db.close || raise_error
46
- end
47
-
48
- # Generates a unique id (in the context of this Table instance)
49
- #
50
- def generate_unique_id
51
- @db.genuid
52
- end
53
- alias :genuid :generate_unique_id
54
-
55
- INDEX_TYPES = {
56
- :lexical => 0,
57
- :decimal => 1,
58
- :void => 9999,
59
- :remove => 9999,
60
- :keep => 1 << 24
61
- }
62
-
63
- # Sets an index on a column of the table.
64
- #
65
- # Types maybe be :lexical or :decimal, use :keep to "add" and
66
- # :remove (or :void) to "remove" an index.
67
- #
68
- # If column_name is :pk or "", the index will be set on the primary key.
69
- #
70
- # Raises an exception in case of failure.
71
- #
72
- def set_index (column_name, *types)
73
-
74
- column_name = '' if column_name == :pk
75
-
76
- i = types.inject(0) { |i, t| i = i | INDEX_TYPES[t]; i }
77
-
78
- @db.setindex(column_name, i) || raise_error
79
- end
80
-
81
- # Inserts a record in the table db
82
- #
83
- # table['pk0'] = [ 'name', 'fred', 'age', '45' ]
84
- # table['pk1'] = { 'name' => 'jeff', 'age' => '46' }
85
- #
86
- # Accepts both a hash or an array (expects the array to be of the
87
- # form [ key, value, key, value, ... ] else it will raise
88
- # an ArgumentError)
89
- #
90
- # Raises an error in case of failure.
91
- #
92
- def []= (pk, h_or_a)
93
-
94
- m = h_or_a.is_a?(Hash) ? h_or_a : Hash[*h_or_a]
95
-
96
- verify_value(m)
97
-
98
- @db.put(pk, m) || raise_error
99
- end
100
-
101
- # Removes an entry in the table
102
- #
103
- # (might raise an error if the delete itself failed, but returns nil
104
- # if there was no entry for the given key)
105
- #
106
- # Raises an error if something went wrong
107
- #
108
- def delete (k)
109
-
110
- # have to work around... :(
111
-
112
- val = @db[k]
113
- return nil unless val
114
-
115
- @db.out(k) || raise_error
116
- val
117
- end
118
-
119
- # Removes all records in this table database
120
- #
121
- # Raises an error if something went wrong
122
- #
123
- def clear
124
-
125
- @db.vanish || raise_error
126
- end
127
-
128
- # Returns an array of all the primary keys in the table
129
- #
130
- # With no options given, this method will return all the keys (strings)
131
- # in a Ruby array.
132
- #
133
- # :prefix --> returns only the keys who match a given string prefix
134
- #
135
- # :limit --> returns a limited number of keys
136
- #
137
- def keys (options={})
138
-
139
- if pref = options[:prefix]
140
-
141
- @db.fwmkeys(pref, options[:limit] || -1)
142
-
143
- else
144
-
145
- limit = options[:limit] || -1
146
- limit = nil if limit < 1
147
-
148
- @db.iterinit
149
-
150
- l = []
151
-
152
- while (k = @db.iternext)
153
- break if limit and l.size >= limit
154
- l << k
155
- end
156
-
157
- l
158
- end
159
- end
160
-
161
- # Deletes all the entries whose key begin with the given prefix.
162
- #
163
- def delete_keys_with_prefix (prefix)
164
-
165
- if @db.respond_to?(:misc)
166
- @db.misc('outlist', @db.fwmkeys(prefix, -1))
167
- else
168
- ks = @db.fwmkeys(prefix, -1) # -1 for no limit
169
- ks.each { |k| self.delete(k) }
170
- end
171
- end
172
-
173
- # Returns a hash { key => record } of all the records matching the
174
- # given keys.
175
- #
176
- def lget (keys)
177
-
178
- if @db.respond_to?(:mget)
179
- @db.mget(keys)
180
- else
181
- keys.inject({}) { |h, k| v = self[k]; h[k] = v if v; h }
182
- end
183
- end
184
-
185
- # Returns the number of records in this table db
186
- #
187
- def size
188
-
189
- @db.rnum
190
- end
191
-
192
- # Prepares a query instance (block is optional)
193
- #
194
- def prepare_query (&block)
195
-
196
- q = TableQuery.new(table_query_class, self)
197
- block.call(q) if block
198
-
199
- q
200
- end
201
-
202
- # Prepares and runs a query, returns an array of hashes (all Ruby)
203
- # (takes care of freeing the query and the result set structures)
204
- #
205
- def query (&block)
206
-
207
- prepare_query(&block).run
208
- end
209
-
210
- # Prepares, runs AND delete all the matching records.
211
- #
212
- def query_delete (&block)
213
-
214
- prepare_query(&block).delete
215
- end
216
-
217
- # Warning : this method is low-level, you probably only need
218
- # to use #transaction and a block.
219
- #
220
- # Direct call for 'transaction begin'.
221
- #
222
- def tranbegin
223
-
224
- @db.tranbegin || raise_error
225
- end
226
-
227
- # Warning : this method is low-level, you probably only need
228
- # to use #transaction and a block.
229
- #
230
- # Direct call for 'transaction commit'.
231
- #
232
- def trancommit
233
-
234
- @db.trancommit || raise_error
235
- end
236
-
237
- # Warning : this method is low-level, you probably only need
238
- # to use #transaction and a block.
239
- #
240
- # Direct call for 'transaction abort'.
241
- #
242
- def tranabort
243
-
244
- @db.tranabort || raise_error
245
- end
246
-
247
- # Returns the underlying 'native' Ruby object (of the class devised by
248
- # Hirabayashi-san)
249
- #
250
- def original
251
-
252
- @db
253
- end
254
-
255
- protected
256
-
257
- # Returns the value (as a Ruby Hash) else nil
258
- #
259
- # (the actual #[] method is provided by HashMethods)
260
- #
261
- def get (k)
262
-
263
- @db.get(k)
264
- end
265
-
266
- # Obviously something went wrong, let's ask the db about it and raise
267
- # an EdoError
268
- #
269
- def raise_error
270
-
271
- err_code = @db.ecode
272
- err_msg = @db.errmsg(err_code)
273
-
274
- raise EdoError.new("(err #{err_code}) #{err_msg}")
275
- end
276
-
277
- def verify_value (h)
278
-
279
- h.each { |k, v|
280
-
281
- next if k.is_a?(String) and v.is_a?(String)
282
-
283
- raise ArgumentError.new(
284
- "only String keys and values are accepted " +
285
- "( #{k.inspect} => #{v.inspect} )")
286
- }
287
- end
288
- end
289
-
290
- #
291
- # A query on a Tokyo Cabinet table db
292
- #
293
- class TableQuery
294
-
295
- include Rufus::Tokyo::QueryConstants
296
-
297
- # Creates a query for a given Rufus::Tokyo::Table
298
- #
299
- # Queries are usually created via the #query (#prepare_query #do_query)
300
- # of the Table instance.
301
- #
302
- # Methods of interest here are :
303
- #
304
- # * #add (or #add_condition)
305
- # * #order_by
306
- # * #limit
307
- #
308
- # also
309
- #
310
- # * #pk_only
311
- # * #no_pk
312
- #
313
- def initialize (query_class, table)
314
-
315
- @table = table
316
- @query = query_class.new(table.original)
317
-
318
- @opts = {}
319
- end
320
-
321
- # Adds a condition
322
- #
323
- # table.query { |q|
324
- # q.add 'name', :equals, 'Oppenheimer'
325
- # q.add 'age', :numgt, 35
326
- # }
327
- #
328
- # Understood 'operators' :
329
- #
330
- # :streq # string equality
331
- # :eq
332
- # :eql
333
- # :equals
334
- #
335
- # :strinc # string include
336
- # :inc # string include
337
- # :includes # string include
338
- #
339
- # :strbw # string begins with
340
- # :bw
341
- # :starts_with
342
- # :strew # string ends with
343
- # :ew
344
- # :ends_with
345
- #
346
- # :strand # string which include all the tokens in the given exp
347
- # :and
348
- #
349
- # :stror # string which include at least one of the tokens
350
- # :or
351
- #
352
- # :stroreq # string which is equal to at least one token
353
- #
354
- # :strorrx # string which matches the given regex
355
- # :regex
356
- # :matches
357
- #
358
- # # numbers...
359
- #
360
- # :numeq # equal
361
- # :numequals
362
- # :numgt # greater than
363
- # :gt
364
- # :numge # greater or equal
365
- # :ge
366
- # :gte
367
- # :numlt # greater or equal
368
- # :lt
369
- # :numle # greater or equal
370
- # :le
371
- # :lte
372
- # :numbt # a number between two tokens in the given exp
373
- # :bt
374
- # :between
375
- #
376
- # :numoreq # number which is equal to at least one token
377
- #
378
- def add (colname, operator, val, affirmative=true, no_index=false)
379
-
380
- op = operator.is_a?(Fixnum) ? operator : OPERATORS[operator]
381
- op = op | TDBQCNEGATE unless affirmative
382
- op = op | TDBQCNOIDX if no_index
383
-
384
- @query.addcond(colname, op, val)
385
- end
386
- alias :add_condition :add
387
-
388
- # Sets the max number of records to return for this query.
389
- #
390
- # (If you're using TC >= 1.4.10 the optional 'offset' (skip) parameter
391
- # is accepted)
392
- #
393
- def limit (i, offset=-1)
394
-
395
- @query.respond_to?(:setlimit) ?
396
- @query.setlimit(i, offset) :
397
- @query.setmax(i)
398
- end
399
-
400
- # Sets the sort order for the result of the query
401
- #
402
- # The 'direction' may be :
403
- #
404
- # :strasc # string ascending
405
- # :strdesc
406
- # :asc # string ascending
407
- # :desc
408
- # :numasc # number ascending
409
- # :numdesc
410
- #
411
- def order_by (colname, direction=:strasc)
412
-
413
- @query.setorder(colname, DIRECTIONS[direction])
414
- end
415
-
416
- # When set to true, only the primary keys of the matching records will
417
- # be returned.
418
- #
419
- def pk_only (on=true)
420
-
421
- @opts[:pk_only] = on
422
- end
423
-
424
- # When set to true, the :pk (primary key) is not inserted in the record
425
- # (hashes) returned
426
- #
427
- def no_pk (on=true)
428
-
429
- @opts[:no_pk] = on
430
- end
431
-
432
- # Process each record using the supplied block, which will be passed
433
- # two parameters, the primary key and the value hash.
434
- #
435
- # The block passed to this method accepts two parameters : the [String]
436
- # primary key and a Hash of the values for the record.
437
- #
438
- # The return value of the passed block does matter. Three different
439
- # values are expected :stop, :delete or a Hash instance.
440
- #
441
- # :stop will make the iteration stop, further matching records will not
442
- # be passed to the block
443
- #
444
- # :delete will let Tokyo Cabinet delete the record just seen.
445
- #
446
- # a Hash is passed to let TC update the values for the record just seen.
447
- #
448
- # Passing an array is possible : [ :stop, { 'name' => 'Toto' } ] will
449
- # update the record just seen to a unique column 'name' and will stop the
450
- # iteration. Likewise, returning [ :stop, :delete ] will work as well.
451
- #
452
- def process (&block)
453
-
454
- @query.proc() do |key, val|
455
-
456
- r = block.call(key, val)
457
- r = [ r ] unless r.is_a?(Array)
458
-
459
- if updated_value = r.find { |e| e.is_a?(Hash) }
460
- val.merge!(updated_value)
461
- end
462
-
463
- r.inject(0) { |i, v|
464
- case v
465
- when :stop then i = i | 1 << 24
466
- when :delete then i = i | 2
467
- when Hash then i = i | 1
468
- end
469
- i
470
- }
471
- end
472
-
473
- self
474
- end
475
-
476
- # Runs this query (returns a TableResultSet instance)
477
- #
478
- def run
479
-
480
- @last_resultset = TableResultSet.new(@table, @query.search, @opts)
481
- end
482
-
483
- # Runs this query AND immediately let the matching records get deleted.
484
- #
485
- def delete
486
-
487
- @query.searchout || @table.raise_error
488
- end
489
-
490
- # Returns the count of results this query return when last run.
491
- # Returns 0 if the query was not yet run.
492
- #
493
- def count
494
-
495
- #@query.count
496
- # not yet implemented by Hirabayashi-san
497
-
498
- @last_resultset ? @last_resultset.size : 0
499
- end
500
-
501
- # Frees this data structure
502
- #
503
- def free
504
-
505
- # nothing ... :( I hope there's no memory leak
506
- end
507
-
508
- alias :close :free
509
- alias :destroy :free
510
- end
511
-
512
- #
513
- # The thing queries return
514
- #
515
- class TableResultSet
516
- include Enumerable
517
-
518
- def initialize (table, primary_keys, query_opts)
519
-
520
- @table = table
521
- @keys = primary_keys
522
- @opts = query_opts
523
- end
524
-
525
- # Returns the count of element in this result set
526
- #
527
- def size
528
-
529
- @keys.size
530
- end
531
-
532
- alias :length :size
533
-
534
- # The classical each
535
- #
536
- def each
537
-
538
- @keys.each do |pk|
539
- if @opts[:pk_only]
540
- yield(pk)
541
- else
542
- val = @table[pk]
543
- val[:pk] = pk unless @opts[:no_pk]
544
- yield(val)
545
- end
546
- end
547
- end
548
-
549
- # Returns an array of hashes
550
- #
551
- def to_a
552
-
553
- self.collect { |m| m }
554
- end
555
-
556
- # Frees this query (the underlying Tokyo Cabinet list structure)
557
- #
558
- def free
559
-
560
- # nothing to do, kept for similarity with Rufus::Tokyo
561
- end
562
-
563
- alias :close :free
564
- alias :destroy :free
565
- end
566
- end
567
-