sdbtools 0.1.0 → 0.2.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.
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/lib/sdbtools.rb +7 -1
- data/lib/selection.rb +189 -0
- data/spec/selection_spec.rb +295 -0
- metadata +15 -2
data/Rakefile
CHANGED
@@ -19,6 +19,7 @@ END
|
|
19
19
|
gem.add_dependency 'highline', '~> 1.5'
|
20
20
|
gem.add_dependency 'progressbar', '~> 0.0.3'
|
21
21
|
gem.add_dependency 'libxml-ruby', '~> 1.1'
|
22
|
+
gem.add_dependency 'arrayfields', '~> 4.7'
|
22
23
|
gem.add_development_dependency "rspec", ">= 1.2.9"
|
23
24
|
end
|
24
25
|
Jeweler::GemcutterTasks.new
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/sdbtools.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'fattr'
|
2
2
|
require 'right_aws'
|
3
|
+
require File.expand_path('selection', File.dirname(__FILE__))
|
3
4
|
|
4
5
|
module SDBTools
|
5
6
|
|
@@ -8,16 +9,21 @@ module SDBTools
|
|
8
9
|
class Operation
|
9
10
|
include Enumerable
|
10
11
|
|
12
|
+
attr_reader :method
|
13
|
+
attr_reader :args
|
14
|
+
attr_reader :starting_token
|
15
|
+
|
11
16
|
def initialize(sdb, method, *args)
|
12
17
|
@options = args.last.is_a?(Hash) ? args.pop : {}
|
13
18
|
@sdb = sdb
|
14
19
|
@method = method
|
15
20
|
@args = args
|
21
|
+
@starting_token = @options[:starting_token]
|
16
22
|
end
|
17
23
|
|
18
24
|
# Yields once for each result set, until there is no next token.
|
19
25
|
def each
|
20
|
-
next_token =
|
26
|
+
next_token = starting_token
|
21
27
|
begin
|
22
28
|
args = @args.dup
|
23
29
|
args << next_token
|
data/lib/selection.rb
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'arrayfields'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module SDBTools
|
5
|
+
class Selection
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
MAX_RESULT_LIMIT = 250
|
9
|
+
DEFAULT_RESULT_LIMIT = 100
|
10
|
+
|
11
|
+
attr_accessor :domain
|
12
|
+
attr_accessor :attributes
|
13
|
+
attr_accessor :conditions
|
14
|
+
attr_reader :limit
|
15
|
+
attr_accessor :offset
|
16
|
+
attr_accessor :order_by
|
17
|
+
attr_accessor :order
|
18
|
+
attr_accessor :sdb
|
19
|
+
attr_accessor :logger
|
20
|
+
|
21
|
+
# For testing
|
22
|
+
attr_writer :starting_token
|
23
|
+
|
24
|
+
def initialize(sdb, domain, options={})
|
25
|
+
@sdb = sdb
|
26
|
+
@domain = domain.to_s
|
27
|
+
@attributes = options.fetch(:attributes) { :all }
|
28
|
+
@conditions = Array(options[:conditions])
|
29
|
+
@order = options.fetch(:order) { :ascending }
|
30
|
+
@order_by = options.fetch(:order_by) { :none }
|
31
|
+
@order_by = @order_by.to_s unless @order_by == :none
|
32
|
+
self.limit = options.fetch(:limit) { DEFAULT_RESULT_LIMIT }
|
33
|
+
@offset = options.fetch(:offset) { 0 }.to_i
|
34
|
+
@logger = options.fetch(:logger){::Logger.new($stderr)}
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_s
|
38
|
+
"SELECT #{output_list} FROM #{quote_name(domain)}#{match_expression}#{sort_instructions}#{limit_clause}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def count_expression
|
42
|
+
"SELECT count(*) FROM #{quote_name(domain)}#{match_expression}#{sort_instructions}#{limit_clause_for_count}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def offset_count_expression
|
46
|
+
"SELECT count(*) FROM #{quote_name(domain)}#{match_expression}#{sort_instructions} LIMIT #{offset}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def count
|
50
|
+
@count ||= count_operation.inject(0){|count, results|
|
51
|
+
count += results[:items].first["Domain"]["Count"].first.to_i
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
alias_method :size, :count
|
56
|
+
alias_method :length, :count
|
57
|
+
|
58
|
+
def each
|
59
|
+
return if limit == 0
|
60
|
+
num_items = 0
|
61
|
+
select_operation.each do |results|
|
62
|
+
results[:items].each do |item|
|
63
|
+
yield(item.keys.first, item.values.first)
|
64
|
+
num_items += 1
|
65
|
+
return if limit != :none && num_items >= limit
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def results
|
71
|
+
@results ||= inject(Arrayfields.new){|results, (name, value)|
|
72
|
+
results[name] = value
|
73
|
+
results
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def count_operation
|
78
|
+
Operation.new(sdb, :select, count_expression, :starting_token => starting_token)
|
79
|
+
end
|
80
|
+
|
81
|
+
def offset_count_operation
|
82
|
+
Operation.new(sdb, :select, offset_count_expression)
|
83
|
+
end
|
84
|
+
|
85
|
+
def select_operation
|
86
|
+
Operation.new(sdb, :select, to_s, :starting_token => starting_token)
|
87
|
+
end
|
88
|
+
|
89
|
+
def starting_token
|
90
|
+
@starting_token ||=
|
91
|
+
case offset
|
92
|
+
when 0 then nil
|
93
|
+
else
|
94
|
+
op = offset_count_operation
|
95
|
+
count = 0
|
96
|
+
op.each do |results|
|
97
|
+
count += results[:items].first["Domain"]["Count"].first.to_i
|
98
|
+
if count == offset || results[:next_token].nil?
|
99
|
+
return results[:next_token]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
raise "Failed to find offset #{offset}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def limit=(new_limit)
|
107
|
+
# We can't yet support large limits. In order to do so, it will be necessary
|
108
|
+
# to implement limit chunking, where the limit is split across multiple
|
109
|
+
# requests of 250 items and a final request of limit % 250 items.
|
110
|
+
case new_limit
|
111
|
+
when :none, (0..MAX_RESULT_LIMIT) then
|
112
|
+
@limit = new_limit
|
113
|
+
else
|
114
|
+
raise RangeError, "Limit must be 0..250 or :none"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def quote_name(name)
|
121
|
+
if name.to_s =~ /^[A-Z$_][A-Z0-9$_]*$/i
|
122
|
+
name.to_s
|
123
|
+
else
|
124
|
+
"`" + name.to_s.gsub("`", "``") + "`"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def quote_value(value)
|
129
|
+
'"' + value.to_s.gsub(/"/, '""') + '"'
|
130
|
+
end
|
131
|
+
|
132
|
+
def output_list
|
133
|
+
case attributes
|
134
|
+
when Array then attributes.map{|a| quote_name(a)}.join(", ")
|
135
|
+
when :all then "*"
|
136
|
+
else raise ScriptError, "Bad attributes: #{attributes.inspect}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def match_expression
|
141
|
+
case conditions.size
|
142
|
+
when 0 then ""
|
143
|
+
else " WHERE #{prepared_conditions.join(' ')}"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def prepared_conditions
|
148
|
+
conditions.map { |condition|
|
149
|
+
case condition
|
150
|
+
when String then condition
|
151
|
+
when Array then
|
152
|
+
values = condition.dup
|
153
|
+
template = values.shift.to_s
|
154
|
+
template.gsub(/\?/) {|match|
|
155
|
+
quote_value(values.shift.to_s)
|
156
|
+
}
|
157
|
+
else
|
158
|
+
raise ScriptError, "Bad condition: #{condition.inspect}"
|
159
|
+
end
|
160
|
+
}
|
161
|
+
end
|
162
|
+
|
163
|
+
def limit_clause
|
164
|
+
case limit
|
165
|
+
when :none then " LIMIT #{MAX_RESULT_LIMIT}"
|
166
|
+
when DEFAULT_RESULT_LIMIT then ""
|
167
|
+
else
|
168
|
+
batch_limit = limit < MAX_RESULT_LIMIT ? limit : MAX_RESULT_LIMIT
|
169
|
+
" LIMIT #{batch_limit}"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def limit_clause_for_count
|
174
|
+
case limit
|
175
|
+
when :none then ""
|
176
|
+
else limit_clause
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def sort_instructions
|
181
|
+
case order_by
|
182
|
+
when :none then ""
|
183
|
+
else
|
184
|
+
direction = (order == :ascending ? "ASC" : "DESC")
|
185
|
+
" ORDER BY #{quote_name(order_by)} #{direction}"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
@@ -0,0 +1,295 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
module SDBTools
|
4
|
+
describe Selection do
|
5
|
+
def count_results(count, token=nil)
|
6
|
+
{
|
7
|
+
:items => [
|
8
|
+
"Domain" => {
|
9
|
+
"Count" => [count.to_s]
|
10
|
+
}
|
11
|
+
],
|
12
|
+
:next_token => token
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def select_results(names, token=nil)
|
17
|
+
{
|
18
|
+
:items => names.map{|n| {n => {"#{n}_attr" => ["#{n}_val"]}}},
|
19
|
+
:next_token => token
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
before :each do
|
24
|
+
@sdb = stub("SDB")
|
25
|
+
end
|
26
|
+
|
27
|
+
context "with domain THE_DOMAIN" do
|
28
|
+
before :each do
|
29
|
+
@it = Selection.new(@sdb, "THE_DOMAIN")
|
30
|
+
end
|
31
|
+
|
32
|
+
specify { @it.to_s.should == "SELECT * FROM THE_DOMAIN" }
|
33
|
+
|
34
|
+
it "should be able to generate a count expression" do
|
35
|
+
@it.count_expression.should be == "SELECT count(*) FROM THE_DOMAIN"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should be able to generate a count operation" do
|
39
|
+
op = @it.count_operation
|
40
|
+
op.method.should be == :select
|
41
|
+
op.args.should be == ["SELECT count(*) FROM THE_DOMAIN"]
|
42
|
+
op.starting_token.should be_nil
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should generate a nil start token" do
|
46
|
+
@it.starting_token.should be_nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "with domain 1DO`MAIN" do
|
51
|
+
before :each do
|
52
|
+
@it = Selection.new(@sdb, "1DO`MAIN")
|
53
|
+
end
|
54
|
+
|
55
|
+
specify { @it.to_s.should == "SELECT * FROM `1DO``MAIN`" }
|
56
|
+
end
|
57
|
+
|
58
|
+
context "with explicit attributes" do
|
59
|
+
before :each do
|
60
|
+
@it = Selection.new(@sdb, "DOMAIN", :attributes => [:foo, :bar])
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should select the attributes" do
|
64
|
+
@it.to_s.should == "SELECT foo, bar FROM DOMAIN"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "with funky attribute names" do
|
69
|
+
before :each do
|
70
|
+
@it = Selection.new(@sdb, "DOMAIN",
|
71
|
+
:attributes => ["attr foo", "1`bar", "baz"])
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should quote the funky attribute names" do
|
75
|
+
@it.to_s.should == %Q{SELECT `attr foo`, `1``bar`, baz FROM DOMAIN}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "with conditions" do
|
80
|
+
before :each do
|
81
|
+
@it = Selection.new(@sdb, "DOMAIN",
|
82
|
+
:attributes => ["attr foo", "bar"],
|
83
|
+
:conditions => [
|
84
|
+
'bar == "buz"',
|
85
|
+
"AND",
|
86
|
+
['`attr foo` between ? and ?', 4, '1"2']])
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should format quote and interpolate the conditions" do
|
90
|
+
@it.to_s.should be ==
|
91
|
+
%Q{SELECT `attr foo`, bar FROM DOMAIN WHERE bar == "buz" AND `attr foo` between "4" and "1""2"}
|
92
|
+
end
|
93
|
+
|
94
|
+
end # "
|
95
|
+
|
96
|
+
context "with a limit of :none" do
|
97
|
+
before :each do
|
98
|
+
@it = Selection.new(@sdb, "DOMAIN",
|
99
|
+
:attributes => [:foo, :bar],
|
100
|
+
:conditions => ["foo == 'bar'"],
|
101
|
+
:limit => :none)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should append a limit clause" do
|
105
|
+
@it.to_s.should be == "SELECT foo, bar FROM DOMAIN WHERE foo == 'bar' LIMIT 250"
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should be able to generate a count expression" do
|
109
|
+
@it.count_expression.should be ==
|
110
|
+
"SELECT count(*) FROM DOMAIN WHERE foo == 'bar'"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "with a limit" do
|
115
|
+
before :each do
|
116
|
+
@it = Selection.new(@sdb, "DOMAIN",
|
117
|
+
:attributes => [:foo, :bar],
|
118
|
+
:conditions => ["foo == 'bar'"],
|
119
|
+
:limit => 10)
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should append a limit clause" do
|
123
|
+
@it.to_s.should be == "SELECT foo, bar FROM DOMAIN WHERE foo == 'bar' LIMIT 10"
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should be able to generate a count expression" do
|
127
|
+
@it.count_expression.should be ==
|
128
|
+
"SELECT count(*) FROM DOMAIN WHERE foo == 'bar' LIMIT 10"
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should limit results" do
|
132
|
+
results = select_results(Array.new(100, "foo"))
|
133
|
+
@sdb.stub!(:select).and_return(results)
|
134
|
+
@it.to_a.size.should == 10
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context "ordered by an attribute" do
|
139
|
+
before :each do
|
140
|
+
@it = Selection.new(@sdb, "DOMAIN",
|
141
|
+
:attributes => [:foo, :bar],
|
142
|
+
:conditions => ["foo == 'bar'"],
|
143
|
+
:order_by => "foo")
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should append an order clause" do
|
147
|
+
@it.to_s.should be ==
|
148
|
+
"SELECT foo, bar FROM DOMAIN WHERE foo == 'bar' ORDER BY foo ASC"
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should be able to generate a count expression" do
|
152
|
+
@it.count_expression.should be ==
|
153
|
+
"SELECT count(*) FROM DOMAIN WHERE foo == 'bar' ORDER BY foo ASC"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context "in descending order" do
|
158
|
+
before :each do
|
159
|
+
@it = Selection.new(@sdb, "DOMAIN",
|
160
|
+
:attributes => [:foo, :bar],
|
161
|
+
:conditions => ["foo == 'bar'"],
|
162
|
+
:order_by => "foo",
|
163
|
+
:order => :descending)
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should append an order clause" do
|
167
|
+
@it.to_s.should be ==
|
168
|
+
"SELECT foo, bar FROM DOMAIN WHERE foo == 'bar' ORDER BY foo DESC"
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should be able to generate a count expression" do
|
172
|
+
@it.count_expression.should be ==
|
173
|
+
"SELECT count(*) FROM DOMAIN WHERE foo == 'bar' ORDER BY foo DESC"
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should be able to generate an offset count expression" do
|
177
|
+
@it.offset_count_expression.should be ==
|
178
|
+
"SELECT count(*) FROM DOMAIN WHERE foo == 'bar' ORDER BY foo DESC LIMIT 0"
|
179
|
+
end
|
180
|
+
|
181
|
+
context "with a limit" do
|
182
|
+
before :each do
|
183
|
+
@it.limit = 250
|
184
|
+
end
|
185
|
+
|
186
|
+
specify { @it.to_s.should be ==
|
187
|
+
"SELECT foo, bar FROM DOMAIN WHERE foo == 'bar' ORDER BY foo DESC LIMIT 250"
|
188
|
+
}
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# We can't yet support large limits. In order to do so, it will be necessary
|
193
|
+
# to implement limit chunking, where the limit is split across multiple
|
194
|
+
# requests of 250 items and a final request of limit % 250 items.
|
195
|
+
it "should reject limits > 250" do
|
196
|
+
lambda do
|
197
|
+
Selection.new(@sdb, "DOMAIN", :limit => 251)
|
198
|
+
end.should raise_error
|
199
|
+
lambda do
|
200
|
+
Selection.new(@sdb, "DOMAIN").limit = 251
|
201
|
+
end.should raise_error
|
202
|
+
end
|
203
|
+
|
204
|
+
context "with an offset" do
|
205
|
+
before :each do
|
206
|
+
@it = Selection.new(@sdb, "DOMAIN",
|
207
|
+
:attributes => [:foo, :bar],
|
208
|
+
:conditions => ["foo == 'bar'"],
|
209
|
+
:limit => 10,
|
210
|
+
:offset => 200)
|
211
|
+
|
212
|
+
# Note: counts are relative to the last count, not absolute
|
213
|
+
@first_results = count_results(100, "TOKEN1")
|
214
|
+
@second_results = count_results(100, "TOKEN2")
|
215
|
+
@third_results = count_results(100, nil)
|
216
|
+
@sdb.stub!(:select).
|
217
|
+
and_return(@first_results, @second_results, @third_results)
|
218
|
+
end
|
219
|
+
|
220
|
+
it "should be able to generate an offset count expression" do
|
221
|
+
@it.offset_count_expression.should be ==
|
222
|
+
"SELECT count(*) FROM DOMAIN WHERE foo == 'bar' LIMIT 200"
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should be able to generate an offset count operation" do
|
226
|
+
op = @it.offset_count_operation
|
227
|
+
op.method.should be == :select
|
228
|
+
op.args.should be == [
|
229
|
+
"SELECT count(*) FROM DOMAIN WHERE foo == 'bar' LIMIT 200"
|
230
|
+
]
|
231
|
+
op.starting_token.should be_nil
|
232
|
+
end
|
233
|
+
|
234
|
+
it "should use an SDB count to determine starting token" do
|
235
|
+
@sdb = stub("SDB")
|
236
|
+
@sdb.should_receive(:select).
|
237
|
+
with(@it.offset_count_expression, nil).
|
238
|
+
ordered.
|
239
|
+
and_return(@first_results)
|
240
|
+
@sdb.should_receive(:select).
|
241
|
+
with(@it.offset_count_expression, "TOKEN1").
|
242
|
+
ordered.
|
243
|
+
and_return(@second_results)
|
244
|
+
@sdb.should_not_receive(:select).
|
245
|
+
with(@it.offset_count_expression, "TOKEN2")
|
246
|
+
@it.sdb = @sdb
|
247
|
+
@it.starting_token.should be == "TOKEN2"
|
248
|
+
end
|
249
|
+
|
250
|
+
it "should be able to generate a select operation" do
|
251
|
+
op = @it.select_operation
|
252
|
+
op.args.should be == [
|
253
|
+
"SELECT foo, bar FROM DOMAIN WHERE foo == 'bar' LIMIT 10"
|
254
|
+
]
|
255
|
+
op.starting_token.should be == "TOKEN2"
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should be able to count items in the selection" do
|
259
|
+
@sdb = stub("SDB")
|
260
|
+
@sdb.should_receive(:select).
|
261
|
+
with(@it.count_expression, "TOKEN2").
|
262
|
+
and_return(count_results(4, "TOKEN5"))
|
263
|
+
@sdb.should_receive(:select).
|
264
|
+
with(@it.count_expression, "TOKEN5").
|
265
|
+
and_return(count_results(5, nil))
|
266
|
+
@it.sdb = @sdb
|
267
|
+
@it.starting_token = "TOKEN2"
|
268
|
+
@it.size.should be == 9
|
269
|
+
@it.length.should be == 9
|
270
|
+
@it.count.should be == 9
|
271
|
+
end
|
272
|
+
|
273
|
+
it "should be able to get selection results" do
|
274
|
+
@sdb = stub("SDB")
|
275
|
+
@sdb.should_receive(:select).
|
276
|
+
with(@it.to_s, "TOKEN2").
|
277
|
+
and_return(select_results(["foo", "bar"], "TOKEN5"))
|
278
|
+
@sdb.should_receive(:select).
|
279
|
+
with(@it.to_s, "TOKEN5").
|
280
|
+
and_return(select_results(["baz", "buz"], nil))
|
281
|
+
@it.sdb = @sdb
|
282
|
+
@it.starting_token = "TOKEN2"
|
283
|
+
@it.results.values.should be == [
|
284
|
+
{ "foo_attr" => ["foo_val"]},
|
285
|
+
{ "bar_attr" => ["bar_val"]},
|
286
|
+
{ "baz_attr" => ["baz_val"]},
|
287
|
+
{ "buz_attr" => ["buz_val"]}
|
288
|
+
]
|
289
|
+
@it.results.keys.should be == [
|
290
|
+
"foo", "bar", "baz", "buz"
|
291
|
+
]
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sdbtools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Avdi Grimm
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-01-
|
12
|
+
date: 2010-01-19 00:00:00 -05:00
|
13
13
|
default_executable: sdbtool
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -72,6 +72,16 @@ dependencies:
|
|
72
72
|
- !ruby/object:Gem::Version
|
73
73
|
version: "1.1"
|
74
74
|
version:
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: arrayfields
|
77
|
+
type: :runtime
|
78
|
+
version_requirement:
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ~>
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: "4.7"
|
84
|
+
version:
|
75
85
|
- !ruby/object:Gem::Dependency
|
76
86
|
name: rspec
|
77
87
|
type: :development
|
@@ -98,7 +108,9 @@ files:
|
|
98
108
|
- VERSION
|
99
109
|
- bin/sdbtool
|
100
110
|
- lib/sdbtools.rb
|
111
|
+
- lib/selection.rb
|
101
112
|
- spec/operation_spec.rb
|
113
|
+
- spec/selection_spec.rb
|
102
114
|
- spec/spec_helper.rb
|
103
115
|
has_rdoc: true
|
104
116
|
homepage: http://github.com/devver/sdbtools
|
@@ -129,5 +141,6 @@ signing_key:
|
|
129
141
|
specification_version: 3
|
130
142
|
summary: A high-level OO interface to Amazon SimpleDB
|
131
143
|
test_files:
|
144
|
+
- spec/selection_spec.rb
|
132
145
|
- spec/operation_spec.rb
|
133
146
|
- spec/spec_helper.rb
|