metry 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +6 -0
- data/Manifest.txt +14 -69
- data/TODO +2 -3
- data/cucumber.yml +1 -0
- data/example/example.rb +1 -1
- data/features/psycho/dashboard.feature +17 -0
- data/features/psycho/goals.feature +24 -0
- data/features/psycho/visitor_tracking.feature +32 -0
- data/features/step_definitions/goals.rb +3 -0
- data/features/step_definitions/tracking.rb +1 -1
- data/features/step_definitions/web.rb +14 -2
- data/features/support/env.rb +4 -0
- data/lib/metry.rb +4 -6
- data/lib/metry/experiment.rb +6 -8
- data/lib/metry/psycho.rb +112 -0
- data/lib/metry/psycho/dashboard.erb +14 -0
- data/lib/metry/psycho/layout.erb +8 -0
- data/lib/metry/psycho/new_goal.erb +9 -0
- data/lib/metry/psycho/visitor.erb +7 -0
- data/lib/metry/rack/tracking.rb +17 -13
- data/lib/metry/storage.rb +116 -0
- data/radiant/example/features/metry.feature +8 -8
- data/radiant/example/features/psycho.feature +13 -0
- data/radiant/example/features/step_definitions/experiments.rb +2 -2
- data/radiant/example/features/step_definitions/tracking.rb +1 -1
- data/radiant/example/features/support/env.rb +3 -0
- data/radiant/extension/lib/metry_authenticator.rb +30 -0
- data/radiant/extension/lib/metry_tags.rb +2 -1
- data/radiant/extension/metry_extension.rb +1 -1
- data/test/{test_tokyo.rb → test_storage.rb} +3 -3
- metadata +17 -72
- data/lib/metry/memory.rb +0 -27
- data/lib/metry/tokyo.rb +0 -181
- data/vendor/rufus-tokyo/CHANGELOG.txt +0 -112
- data/vendor/rufus-tokyo/CREDITS.txt +0 -27
- data/vendor/rufus-tokyo/LICENSE.txt +0 -21
- data/vendor/rufus-tokyo/README.txt +0 -310
- data/vendor/rufus-tokyo/Rakefile +0 -118
- data/vendor/rufus-tokyo/TODO.txt +0 -25
- data/vendor/rufus-tokyo/doc/decision_table.numbers +0 -0
- data/vendor/rufus-tokyo/doc/rdoc-style.css +0 -320
- data/vendor/rufus-tokyo/lib/rufus-edo.rb +0 -3
- data/vendor/rufus-tokyo/lib/rufus-tokyo.rb +0 -3
- data/vendor/rufus-tokyo/lib/rufus/edo.rb +0 -39
- data/vendor/rufus-tokyo/lib/rufus/edo/README.txt +0 -106
- data/vendor/rufus-tokyo/lib/rufus/edo/cabcore.rb +0 -333
- data/vendor/rufus-tokyo/lib/rufus/edo/cabinet/abstract.rb +0 -219
- data/vendor/rufus-tokyo/lib/rufus/edo/cabinet/table.rb +0 -159
- data/vendor/rufus-tokyo/lib/rufus/edo/error.rb +0 -36
- data/vendor/rufus-tokyo/lib/rufus/edo/ntyrant.rb +0 -4
- data/vendor/rufus-tokyo/lib/rufus/edo/ntyrant/abstract.rb +0 -137
- data/vendor/rufus-tokyo/lib/rufus/edo/ntyrant/table.rb +0 -141
- data/vendor/rufus-tokyo/lib/rufus/edo/tabcore.rb +0 -567
- data/vendor/rufus-tokyo/lib/rufus/tokyo.rb +0 -58
- data/vendor/rufus-tokyo/lib/rufus/tokyo/cabinet/abstract.rb +0 -568
- data/vendor/rufus-tokyo/lib/rufus/tokyo/cabinet/lib.rb +0 -230
- data/vendor/rufus-tokyo/lib/rufus/tokyo/cabinet/table.rb +0 -753
- data/vendor/rufus-tokyo/lib/rufus/tokyo/cabinet/util.rb +0 -425
- data/vendor/rufus-tokyo/lib/rufus/tokyo/config.rb +0 -161
- data/vendor/rufus-tokyo/lib/rufus/tokyo/dystopia.rb +0 -43
- data/vendor/rufus-tokyo/lib/rufus/tokyo/dystopia/lib.rb +0 -65
- data/vendor/rufus-tokyo/lib/rufus/tokyo/dystopia/words.rb +0 -71
- data/vendor/rufus-tokyo/lib/rufus/tokyo/hmethods.rb +0 -111
- data/vendor/rufus-tokyo/lib/rufus/tokyo/query.rb +0 -102
- data/vendor/rufus-tokyo/lib/rufus/tokyo/transactions.rb +0 -74
- data/vendor/rufus-tokyo/lib/rufus/tokyo/ttcommons.rb +0 -59
- data/vendor/rufus-tokyo/lib/rufus/tokyo/tyrant.rb +0 -35
- data/vendor/rufus-tokyo/lib/rufus/tokyo/tyrant/abstract.rb +0 -146
- data/vendor/rufus-tokyo/lib/rufus/tokyo/tyrant/lib.rb +0 -153
- data/vendor/rufus-tokyo/lib/rufus/tokyo/tyrant/table.rb +0 -162
- data/vendor/rufus-tokyo/rufus-tokyo.gemspec +0 -25
- data/vendor/rufus-tokyo/spec/cabinet_spec.rb +0 -472
- data/vendor/rufus-tokyo/spec/cabinetconfig_spec.rb +0 -82
- data/vendor/rufus-tokyo/spec/edo_cabinet_spec.rb +0 -447
- data/vendor/rufus-tokyo/spec/edo_ntyrant_spec.rb +0 -299
- data/vendor/rufus-tokyo/spec/edo_ntyrant_table_spec.rb +0 -462
- data/vendor/rufus-tokyo/spec/edo_table_spec.rb +0 -560
- data/vendor/rufus-tokyo/spec/hmethods_spec.rb +0 -44
- data/vendor/rufus-tokyo/spec/incr.lua +0 -20
- data/vendor/rufus-tokyo/spec/spec.rb +0 -9
- data/vendor/rufus-tokyo/spec/spec_base.rb +0 -23
- data/vendor/rufus-tokyo/spec/start_tyrants.sh +0 -26
- data/vendor/rufus-tokyo/spec/stop_tyrants.sh +0 -9
- data/vendor/rufus-tokyo/spec/table_spec.rb +0 -567
- data/vendor/rufus-tokyo/spec/tyrant_spec.rb +0 -309
- data/vendor/rufus-tokyo/spec/tyrant_table_spec.rb +0 -479
- data/vendor/rufus-tokyo/spec/util_list_spec.rb +0 -200
- data/vendor/rufus-tokyo/spec/util_map_spec.rb +0 -132
- data/vendor/rufus-tokyo/tasks/dev.rb +0 -70
- data/vendor/rufus-tokyo/test/bm0.rb +0 -353
- data/vendor/rufus-tokyo/test/bm1_compression.rb +0 -54
- data/vendor/rufus-tokyo/test/con0.rb +0 -30
- data/vendor/rufus-tokyo/test/mem.rb +0 -49
- data/vendor/rufus-tokyo/test/mem1.rb +0 -44
- data/vendor/rufus-tokyo/test/readme0.rb +0 -17
- data/vendor/rufus-tokyo/test/readme1.rb +0 -21
- data/vendor/rufus-tokyo/test/readme2.rb +0 -15
- data/vendor/rufus-tokyo/test/readme3.rb +0 -24
- 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
|
-
|