activerecord-simpledb-adapter 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +20 -0
- data/README.md +50 -0
- data/activerecord-simpledb-adapter.gemspec +80 -0
- data/lib/active_record/connection_adapters/simpledb_adapter.rb +410 -0
- data/lib/activerecord-simpledb-adapter.rb +12 -0
- data/lib/arel/visitors/simpledb.rb +121 -0
- data/lib/tasks/simpledb.rake +17 -0
- data/spec/activerecord-simpledb-adapter_spec.rb +141 -0
- data/spec/spec_helper.rb +59 -0
- metadata +246 -0
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Ilia Ablamonov, Alex Gorkunov, Cloud Castle Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# ABOUT
|
2
|
+
|
3
|
+
Amazon SimpleDB Adapter for ActiveRecord for Rails3
|
4
|
+
|
5
|
+
# FEATURES
|
6
|
+
|
7
|
+
- All data saves in one sdb domain (each activerecord model class detected by special reserved column 'collection')
|
8
|
+
- Columns with numbers (integer and float) saves with special shift and constant size for correcting comparation in SELECT query.
|
9
|
+
|
10
|
+
# USAGE
|
11
|
+
|
12
|
+
install:
|
13
|
+
|
14
|
+
gem install activerecord-simpledb-adapter
|
15
|
+
|
16
|
+
config/database.yml:
|
17
|
+
|
18
|
+
defaults: &defaults
|
19
|
+
adapter: simpledb
|
20
|
+
access_key_id: KEY
|
21
|
+
secret_access_key: SECRET
|
22
|
+
development:
|
23
|
+
<<: *defaults
|
24
|
+
domain_name: domain_name
|
25
|
+
|
26
|
+
model example:
|
27
|
+
Person < ActiveRecord::Base
|
28
|
+
columns_definition do |t|
|
29
|
+
t.string :login
|
30
|
+
t.integer :year, :limit => 4
|
31
|
+
t.boolean :active
|
32
|
+
t.string :from, :to => 'from_property'
|
33
|
+
t.float :price
|
34
|
+
t.integer :lock_version
|
35
|
+
|
36
|
+
t.timestamps
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# LICENSE
|
41
|
+
|
42
|
+
(The MIT License)
|
43
|
+
|
44
|
+
Copyright © Ilia Ablamonov, Alex Gorkunov, Cloud Castle Inc.
|
45
|
+
|
46
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
47
|
+
|
48
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
49
|
+
|
50
|
+
THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{activerecord-simpledb-adapter}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Ilia Ablamonov", "Alex Gorkunov", "Cloud Castle Inc."]
|
12
|
+
s.date = %q{2011-01-14}
|
13
|
+
s.email = %q{ilia@flamefork.ru}
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"LICENSE.txt",
|
16
|
+
"README.md"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
"LICENSE.txt",
|
20
|
+
"README.md",
|
21
|
+
"activerecord-simpledb-adapter.gemspec",
|
22
|
+
"lib/active_record/connection_adapters/simpledb_adapter.rb",
|
23
|
+
"lib/activerecord-simpledb-adapter.rb",
|
24
|
+
"lib/arel/visitors/simpledb.rb",
|
25
|
+
"lib/tasks/simpledb.rake"
|
26
|
+
]
|
27
|
+
s.homepage = %q{http://github.com/cloudcastle/activerecord-simpledb-adapter}
|
28
|
+
s.licenses = ["MIT"]
|
29
|
+
s.require_paths = ["lib"]
|
30
|
+
s.rubygems_version = %q{1.3.7}
|
31
|
+
s.summary = %q{ActiveRecord SimpleDB adapter}
|
32
|
+
s.test_files = [
|
33
|
+
"spec/activerecord-simpledb-adapter_spec.rb",
|
34
|
+
"spec/spec_helper.rb"
|
35
|
+
]
|
36
|
+
|
37
|
+
if s.respond_to? :specification_version then
|
38
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
39
|
+
s.specification_version = 3
|
40
|
+
|
41
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
42
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
|
43
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
44
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
|
45
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
46
|
+
s.add_development_dependency(%q<aws>, [">= 0"])
|
47
|
+
s.add_development_dependency(%q<activerecord>, [">= 0"])
|
48
|
+
s.add_development_dependency(%q<activesupport>, [">= 0"])
|
49
|
+
s.add_runtime_dependency(%q<aws>, ["~> 2.3.0"])
|
50
|
+
s.add_runtime_dependency(%q<activerecord>, ["~> 3.0.3"])
|
51
|
+
s.add_runtime_dependency(%q<activesupport>, ["~> 3.0.3"])
|
52
|
+
s.add_runtime_dependency(%q<uuidtools>, ["~> 2.1.1"])
|
53
|
+
else
|
54
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
55
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
56
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
57
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
58
|
+
s.add_dependency(%q<aws>, [">= 0"])
|
59
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
60
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
61
|
+
s.add_dependency(%q<aws>, ["~> 2.3.0"])
|
62
|
+
s.add_dependency(%q<activerecord>, ["~> 3.0.3"])
|
63
|
+
s.add_dependency(%q<activesupport>, ["~> 3.0.3"])
|
64
|
+
s.add_dependency(%q<uuidtools>, ["~> 2.1.1"])
|
65
|
+
end
|
66
|
+
else
|
67
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
68
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
69
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
70
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
71
|
+
s.add_dependency(%q<aws>, [">= 0"])
|
72
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
73
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
74
|
+
s.add_dependency(%q<aws>, ["~> 2.3.0"])
|
75
|
+
s.add_dependency(%q<activerecord>, ["~> 3.0.3"])
|
76
|
+
s.add_dependency(%q<activesupport>, ["~> 3.0.3"])
|
77
|
+
s.add_dependency(%q<uuidtools>, ["~> 2.1.1"])
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
@@ -0,0 +1,410 @@
|
|
1
|
+
require 'active_record/connection_adapters/abstract_adapter'
|
2
|
+
require 'active_support/core_ext/kernel/requires'
|
3
|
+
require 'active_support/core_ext/hash'
|
4
|
+
require 'uuidtools'
|
5
|
+
|
6
|
+
class Aws::SdbInterface
|
7
|
+
def put_attributes(domain_name, item_name, attributes, replace = false, expected_attributes = {})
|
8
|
+
params = params_with_attributes(domain_name, item_name, attributes, replace, expected_attributes)
|
9
|
+
link = generate_request("PutAttributes", params)
|
10
|
+
request_info( link, QSdbSimpleParser.new )
|
11
|
+
rescue Exception
|
12
|
+
on_exception
|
13
|
+
end
|
14
|
+
|
15
|
+
def delete_attributes(domain_name, item_name, attributes = nil, expected_attributes = {})
|
16
|
+
params = params_with_attributes(domain_name, item_name, attributes, false, expected_attributes)
|
17
|
+
link = generate_request("DeleteAttributes", params)
|
18
|
+
request_info( link, QSdbSimpleParser.new )
|
19
|
+
rescue Exception
|
20
|
+
on_exception
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def pack_expected_attributes(attributes) #:nodoc:
|
25
|
+
{}.tap do |result|
|
26
|
+
idx = 0
|
27
|
+
attributes.each do |attribute, value|
|
28
|
+
v = value.is_a?(Array) ? value.first : value
|
29
|
+
result["Expected.#{idx}.Name"] = attribute.to_s
|
30
|
+
result["Expected.#{idx}.Value"] = ruby_to_sdb(v)
|
31
|
+
idx += 1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def pack_attributes(attributes = {}, replace = false, key_prefix = "")
|
37
|
+
{}.tap do |result|
|
38
|
+
idx = 0
|
39
|
+
if attributes
|
40
|
+
attributes.each do |attribute, value|
|
41
|
+
v = value.is_a?(Array) ? value.first : value
|
42
|
+
result["#{key_prefix}Attribute.#{idx}.Replace"] = 'true' if replace
|
43
|
+
result["#{key_prefix}Attribute.#{idx}.Name"] = attribute
|
44
|
+
result["#{key_prefix}Attribute.#{idx}.Value"] = ruby_to_sdb(v)
|
45
|
+
idx += 1
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def params_with_attributes(domain_name, item_name, attributes, replace, expected_attrubutes)
|
52
|
+
{}.tap do |p|
|
53
|
+
p['DomainName'] = domain_name
|
54
|
+
p['ItemName'] = item_name
|
55
|
+
p.merge!(pack_attributes(attributes, replace)).merge!(pack_expected_attributes(expected_attrubutes))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
module ActiveRecord
|
61
|
+
class Base
|
62
|
+
def self.simpledb_connection(config) # :nodoc:
|
63
|
+
require 'aws'
|
64
|
+
|
65
|
+
config = config.symbolize_keys
|
66
|
+
|
67
|
+
ConnectionAdapters::SimpleDBAdapter.new nil, logger,
|
68
|
+
config[:access_key_id],
|
69
|
+
config[:secret_access_key],
|
70
|
+
config[:domain_name],
|
71
|
+
{
|
72
|
+
:server => config[:host],
|
73
|
+
:port => config[:port],
|
74
|
+
:protocol => config[:protocol],
|
75
|
+
:logger => logger
|
76
|
+
},
|
77
|
+
config
|
78
|
+
end
|
79
|
+
|
80
|
+
DEFAULT_COLLECTION_COLUMN_NAME = "collection".freeze
|
81
|
+
|
82
|
+
def self.columns_definition options = {}
|
83
|
+
table_definition = ConnectionAdapters::SimpleDbTableDifinition.new(options[:collection_column_name] || DEFAULT_COLLECTION_COLUMN_NAME)
|
84
|
+
table_definition.primary_key(Base.get_primary_key(table_name.to_s.singularize))
|
85
|
+
yield table_definition if block_given?
|
86
|
+
|
87
|
+
ConnectionAdapters::SimpleDBAdapter.set_collection_columns table_name, table_definition
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
module Validations
|
95
|
+
|
96
|
+
class UniquenessValidator < ActiveModel::EachValidator
|
97
|
+
|
98
|
+
def validate_each(record, attribute, value)
|
99
|
+
finder_class = find_finder_class_for(record)
|
100
|
+
table = finder_class.unscoped
|
101
|
+
|
102
|
+
table_name = record.class.quoted_table_name
|
103
|
+
|
104
|
+
if value && record.class.serialized_attributes.key?(attribute.to_s)
|
105
|
+
value = YAML.dump value
|
106
|
+
end
|
107
|
+
|
108
|
+
sql, params = mount_sql_and_params(finder_class, table_name, attribute, value)
|
109
|
+
|
110
|
+
relation = table.where(sql, *params)
|
111
|
+
|
112
|
+
Array.wrap(options[:scope]).each do |scope_item|
|
113
|
+
scope_value = record.send(scope_item)
|
114
|
+
relation = relation.where(scope_item => scope_value)
|
115
|
+
end
|
116
|
+
|
117
|
+
if record.persisted?
|
118
|
+
# TODO : This should be in Arel
|
119
|
+
relation = relation.where("#{record.class.primary_key} != ?", record.send(:id))
|
120
|
+
end
|
121
|
+
|
122
|
+
if relation.exists?
|
123
|
+
record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
protected
|
128
|
+
def mount_sql_and_params(klass, table_name, attribute, value) #:nodoc:
|
129
|
+
column = klass.columns_hash[attribute.to_s]
|
130
|
+
|
131
|
+
sql_attribute = "#{table_name}.#{klass.connection.quote_column_name(column.db_column_name)}"
|
132
|
+
sql = "#{sql_attribute} = ?"
|
133
|
+
if value.nil?
|
134
|
+
[sql, [value]]
|
135
|
+
elsif (options[:case_sensitive] || !column.text?)
|
136
|
+
[sql, [value]]
|
137
|
+
else
|
138
|
+
[sql, [value.downcase]]
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
module ConnectionAdapters
|
146
|
+
|
147
|
+
class SimpleDbTableDifinition < TableDefinition
|
148
|
+
|
149
|
+
attr_reader :collection_column_name
|
150
|
+
|
151
|
+
def initialize collection_column_name
|
152
|
+
super nil
|
153
|
+
@collection_column_name = collection_column_name
|
154
|
+
end
|
155
|
+
|
156
|
+
def xml_column_fallback(*args)
|
157
|
+
raise ConfigurationError, "Not supported"
|
158
|
+
end
|
159
|
+
|
160
|
+
def [](name)
|
161
|
+
@columns.find {|column| column.db_column_name == name.to_s}
|
162
|
+
end
|
163
|
+
|
164
|
+
def column(name, type, options = {})
|
165
|
+
raise ConfigurationError, %Q(column '#{collection_column_name}' reserved, please change column name) if name.to_s == collection_column_name
|
166
|
+
@columns << SimpleDBColumn.new(name.to_s, type.to_sym, options[:limit], options[:precision], options[:to])
|
167
|
+
self
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
class SimpleDBColumn < Column
|
173
|
+
|
174
|
+
DEFAULT_NUMBER_LIMIT = 4
|
175
|
+
DEFAULT_FLOAT_PRECISION = 4
|
176
|
+
|
177
|
+
def initialize(name, type, limit = nil, pricision = nil, to = nil)
|
178
|
+
super name, nil, type, true
|
179
|
+
@limit = limit if limit.present?
|
180
|
+
@precision = precision if precision.present?
|
181
|
+
@to = to
|
182
|
+
end
|
183
|
+
|
184
|
+
def quote_number value
|
185
|
+
case sql_type
|
186
|
+
when :float then
|
187
|
+
sprintf("%.#{number_precision}f", number_shift + value.to_f)
|
188
|
+
else
|
189
|
+
(number_shift + value.to_i).to_s
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def unquote_number value
|
194
|
+
case sql_type
|
195
|
+
when :integer then
|
196
|
+
value.to_i - number_shift
|
197
|
+
when :float then
|
198
|
+
precision_part = 10 ** number_precision
|
199
|
+
((value.to_f - number_shift) * precision_part).round / precision_part.to_f
|
200
|
+
else
|
201
|
+
value
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def db_column_name
|
206
|
+
@to || name
|
207
|
+
end
|
208
|
+
|
209
|
+
private
|
210
|
+
def number_shift
|
211
|
+
5 * 10 ** (limit || DEFAULT_NUMBER_LIMIT)
|
212
|
+
end
|
213
|
+
|
214
|
+
def number_precision
|
215
|
+
@precision || DEFAULT_FLOAT_PRECISION
|
216
|
+
end
|
217
|
+
|
218
|
+
def simplified_type(field_type)
|
219
|
+
t = field_type.to_s
|
220
|
+
if t == "primary_key"
|
221
|
+
:string
|
222
|
+
else
|
223
|
+
super(t)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
class SimpleDBAdapter < AbstractAdapter
|
229
|
+
|
230
|
+
@@collections = {}
|
231
|
+
@@ccn = {}
|
232
|
+
|
233
|
+
def self.set_collection_columns table_name, columns_definition
|
234
|
+
@@collections[table_name] = columns_definition
|
235
|
+
@@ccn[table_name] = columns_definition.collection_column_name
|
236
|
+
end
|
237
|
+
|
238
|
+
def columns_definition table_name
|
239
|
+
@@collections[table_name]
|
240
|
+
end
|
241
|
+
|
242
|
+
def collection_column_name table_name
|
243
|
+
@@ccn[table_name]
|
244
|
+
end
|
245
|
+
|
246
|
+
ADAPTER_NAME = 'SimpleDB'.freeze
|
247
|
+
|
248
|
+
def adapter_name
|
249
|
+
ADAPTER_NAME
|
250
|
+
end
|
251
|
+
|
252
|
+
NIL_REPRESENTATION = "Aws::Nil".freeze
|
253
|
+
|
254
|
+
def nil_representation
|
255
|
+
NIL_REPRESENTATION
|
256
|
+
end
|
257
|
+
|
258
|
+
|
259
|
+
def supports_count_distinct?; false; end
|
260
|
+
|
261
|
+
#========= QUOTING =====================
|
262
|
+
|
263
|
+
#dirty hack for removing all (') from value for hash table attrubutes
|
264
|
+
def hash_value_quote(value, column = nil)
|
265
|
+
return nil if value.nil?
|
266
|
+
quote(value, column).gsub /^'*|'*$/, ''
|
267
|
+
end
|
268
|
+
|
269
|
+
def quote(value, column = nil)
|
270
|
+
if value.present? && column.present? && column.number?
|
271
|
+
"'#{column.quote_number value}'"
|
272
|
+
elsif value.nil?
|
273
|
+
"'#{nil_representation}'"
|
274
|
+
else
|
275
|
+
super
|
276
|
+
end
|
277
|
+
end
|
278
|
+
#=======================================
|
279
|
+
|
280
|
+
attr_reader :domain_name
|
281
|
+
|
282
|
+
def initialize(connection, logger, aws_key, aws_secret, domain_name, connection_parameters, config)
|
283
|
+
super(connection, logger)
|
284
|
+
@config = config
|
285
|
+
@domain_name = domain_name
|
286
|
+
@connection_parameters = [
|
287
|
+
aws_key,
|
288
|
+
aws_secret,
|
289
|
+
connection_parameters.merge(:nil_representation => nil_representation)
|
290
|
+
]
|
291
|
+
connect
|
292
|
+
end
|
293
|
+
|
294
|
+
def connect
|
295
|
+
@connection = Aws::SdbInterface.new *@connection_parameters
|
296
|
+
end
|
297
|
+
|
298
|
+
def tables
|
299
|
+
@@collections.keys
|
300
|
+
end
|
301
|
+
|
302
|
+
def columns table_name, name = nil
|
303
|
+
@@collections[table_name].columns
|
304
|
+
end
|
305
|
+
|
306
|
+
def primary_key _
|
307
|
+
'id'
|
308
|
+
end
|
309
|
+
|
310
|
+
def execute sql, name = nil, skip_logging = false
|
311
|
+
p sql
|
312
|
+
case sql[:action]
|
313
|
+
when :insert
|
314
|
+
item_name = get_id sql[:attrs]
|
315
|
+
item_name = sql[:attrs][:id] = generate_id unless item_name
|
316
|
+
@connection.put_attributes domain_name, item_name, sql[:attrs], true
|
317
|
+
@last_insert_id = item_name
|
318
|
+
when :update
|
319
|
+
item_name = get_id sql[:wheres], true
|
320
|
+
@connection.put_attributes domain_name, item_name, sql[:attrs], true, sql[:wheres]
|
321
|
+
when :delete
|
322
|
+
item_name = get_id sql[:wheres], true
|
323
|
+
@connection.delete_attributes domain_name, item_name, nil, sql[:wheres]
|
324
|
+
else
|
325
|
+
raise "Unsupported action: #{sql[:action].inspect}"
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
def insert_sql sql, name = nil, pk = nil, id_value = nil, sequence_name = nil
|
330
|
+
super || @last_insert_id
|
331
|
+
end
|
332
|
+
alias :create :insert_sql
|
333
|
+
|
334
|
+
def select sql, name = nil
|
335
|
+
puts sql
|
336
|
+
result = []
|
337
|
+
response = @connection.select(sql, nil, true)
|
338
|
+
collection_name = get_collection_column_and_name(sql)
|
339
|
+
columns = columns_definition(collection_name)
|
340
|
+
|
341
|
+
response[:items].each do |item|
|
342
|
+
item.each do |id, attrs|
|
343
|
+
ritem = {}
|
344
|
+
ritem['id'] = id unless id == 'Domain' && attrs['Count'] # unless count(*) result
|
345
|
+
attrs.each {|k, vs|
|
346
|
+
column = columns[k]
|
347
|
+
if column.present?
|
348
|
+
ritem[column.name] = column.unquote_number(vs.first)
|
349
|
+
else
|
350
|
+
ritem[k] = vs.first
|
351
|
+
end
|
352
|
+
}
|
353
|
+
puts ritem.inspect
|
354
|
+
result << ritem
|
355
|
+
end
|
356
|
+
end
|
357
|
+
# puts "Box usage: #{response[:box_usage].to_f}"
|
358
|
+
result
|
359
|
+
end
|
360
|
+
|
361
|
+
# Executes the update statement and returns the number of rows affected.
|
362
|
+
def update_sql(sql, name = nil)
|
363
|
+
begin
|
364
|
+
execute(sql, name)
|
365
|
+
1
|
366
|
+
rescue Aws::AwsError => ex
|
367
|
+
#if not conflict state raise
|
368
|
+
raise if ex.http_code != '409'
|
369
|
+
0
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
# Executes the delete statement and returns the number of rows affected.
|
374
|
+
def delete_sql(sql, name = nil)
|
375
|
+
update_sql(sql, name)
|
376
|
+
end
|
377
|
+
|
378
|
+
def create_domain domain_name
|
379
|
+
@connection.create_domain domain_name
|
380
|
+
end
|
381
|
+
|
382
|
+
def delete_domain domain_name
|
383
|
+
@connection.delete_domain domain_name
|
384
|
+
end
|
385
|
+
|
386
|
+
private
|
387
|
+
|
388
|
+
def generate_id
|
389
|
+
UUIDTools::UUID.timestamp_create().to_s
|
390
|
+
end
|
391
|
+
|
392
|
+
def get_id hash, delete_id = false
|
393
|
+
if delete_id
|
394
|
+
hash.delete(:id) || hash.delete('id')
|
395
|
+
else
|
396
|
+
hash[:id] || hash['id']
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
def get_collection_column_and_name sql
|
401
|
+
if sql.match /(#{@@ccn.values.join("|")})\s*=\s*'(.*?)'/
|
402
|
+
$2
|
403
|
+
else
|
404
|
+
raise PreparedStatementInvalid, "collection column '#{@@ccn.values.join(" or ")}' not found in the WHERE section in query"
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module ActiveRecordSimpledbAdapter
|
2
|
+
class Railtie < ::Rails::Railtie
|
3
|
+
rake_tasks do
|
4
|
+
load 'tasks/simpledb.rake'
|
5
|
+
end
|
6
|
+
|
7
|
+
ActiveSupport.on_load(:active_record) do
|
8
|
+
require 'active_record/connection_adapters/simpledb_adapter'
|
9
|
+
require 'arel/visitors/simpledb'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'arel/visitors/to_sql'
|
2
|
+
module Arel
|
3
|
+
module Visitors
|
4
|
+
class SimpleDB < Arel::Visitors::ToSql
|
5
|
+
private
|
6
|
+
|
7
|
+
def hash_value_quote value, column = nil
|
8
|
+
@connection.hash_value_quote value, column
|
9
|
+
end
|
10
|
+
|
11
|
+
def visit_Arel_Nodes_DeleteStatement o
|
12
|
+
{
|
13
|
+
:action => :delete,
|
14
|
+
:wheres => merge_to_hash(o.wheres)
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def visit_Arel_Nodes_UpdateStatement o
|
19
|
+
{
|
20
|
+
:action => :update,
|
21
|
+
:attrs => merge_to_hash(o.values),
|
22
|
+
:wheres => merge_to_hash(o.wheres)
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def visit_Arel_Nodes_InsertStatement o
|
27
|
+
collection_name = visit(o.relation)
|
28
|
+
ccn = @connection.collection_column_name(collection_name)
|
29
|
+
|
30
|
+
{
|
31
|
+
:action => :insert,
|
32
|
+
:attrs => visit(o.values).merge(ccn => collection_name)
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def visit_Arel_Nodes_SelectStatement o
|
37
|
+
o.orders.each do |order|
|
38
|
+
attr = visit(order).to_s.gsub(/ASC|DESC/, '').strip
|
39
|
+
# The sort attribute must be present in predicates
|
40
|
+
o.cores.first.wheres << SqlLiteral.new("#{attr} IS NOT NULL")
|
41
|
+
end
|
42
|
+
collection = o.cores.first.froms.name
|
43
|
+
o.cores.first.wheres << SqlLiteral.new("#{@connection.collection_column_name(collection)} = #{quote(collection)}")
|
44
|
+
o.cores.first.froms = Table.new @connection.domain_name
|
45
|
+
super
|
46
|
+
end
|
47
|
+
|
48
|
+
def visit_Arel_Nodes_Values o
|
49
|
+
result = {}
|
50
|
+
o.expressions.zip(o.columns).map {|value, column|
|
51
|
+
result[column.column.db_column_name] = hash_value_quote(value, column.column)
|
52
|
+
}
|
53
|
+
result
|
54
|
+
end
|
55
|
+
|
56
|
+
def visit_Arel_Nodes_Grouping o
|
57
|
+
visit(o.expr)
|
58
|
+
end
|
59
|
+
|
60
|
+
def visit_Arel_Nodes_And o
|
61
|
+
visit(o.left).merge(visit(o.right)).tap{|m| m.override_to_s super}
|
62
|
+
end
|
63
|
+
|
64
|
+
def visit_Arel_Nodes_Assignment o
|
65
|
+
right = o.right ? hash_value_quote(o.right, o.left.column) : nil
|
66
|
+
{visit(o.left) => right}
|
67
|
+
end
|
68
|
+
|
69
|
+
def visit_Arel_Nodes_Equality o
|
70
|
+
right = o.right ? hash_value_quote(o.right, o.left.column) : nil
|
71
|
+
{visit(o.left) => right}.tap { |m|
|
72
|
+
m.override_to_s "#{visit o.left} = #{quote(o.right, o.left.column)}"
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
def visit_Arel_Nodes_NotEqual o
|
77
|
+
"#{visit o.left} != #{o.right.nil? ? @connection.nil_representation : visit(o.right)}"
|
78
|
+
end
|
79
|
+
|
80
|
+
def visit_Arel_Attributes_Attribute o
|
81
|
+
# Do not use table. prefix for attribute names
|
82
|
+
@last_column = o.column
|
83
|
+
quote_column_name o.column.db_column_name
|
84
|
+
end
|
85
|
+
alias :visit_Arel_Attributes_Integer :visit_Arel_Attributes_Attribute
|
86
|
+
alias :visit_Arel_Attributes_Float :visit_Arel_Attributes_Attribute
|
87
|
+
alias :visit_Arel_Attributes_Decimal :visit_Arel_Attributes_Attribute
|
88
|
+
alias :visit_Arel_Attributes_String :visit_Arel_Attributes_Attribute
|
89
|
+
alias :visit_Arel_Attributes_Time :visit_Arel_Attributes_Attribute
|
90
|
+
alias :visit_Arel_Attributes_Boolean :visit_Arel_Attributes_Attribute
|
91
|
+
|
92
|
+
def visit_Arel_Nodes_SqlLiteral o
|
93
|
+
# Strip table name from table.column -like literals
|
94
|
+
result = o.to_s.gsub(/\w+\.([\w*]+)/, '\1')
|
95
|
+
|
96
|
+
if result.match /(\w+)\s*=\s*'(.*?)'/
|
97
|
+
# transform 'a = b' to {'a' => 'b'}
|
98
|
+
{$1 => $2}.tap {|m| m.override_to_s result}
|
99
|
+
else
|
100
|
+
result
|
101
|
+
end
|
102
|
+
end
|
103
|
+
alias :visit_Arel_SqlLiteral :visit_Arel_Nodes_SqlLiteral
|
104
|
+
|
105
|
+
|
106
|
+
def merge_to_hash a
|
107
|
+
a.map{|x| visit(x)}.inject({}, &:merge)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
VISITORS['simpledb'] = Arel::Visitors::SimpleDB
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class Object
|
116
|
+
def override_to_s s
|
117
|
+
@override_to_s = s
|
118
|
+
#noinspection RubyResolve
|
119
|
+
def self.to_s; @override_to_s; end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
Rake::Task['db:create'].clear
|
2
|
+
Rake::Task['db:seed'].clear
|
3
|
+
namespace :db do
|
4
|
+
task :create => :load_config do
|
5
|
+
create_domain(ActiveRecord::Base.configurations[Rails.env])
|
6
|
+
end
|
7
|
+
|
8
|
+
task :seed => :environment do
|
9
|
+
seed_file = File.join(Rails.root, 'db', 'seeds.rb')
|
10
|
+
load(seed_file) if File.exist?(seed_file)
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_domain(config)
|
14
|
+
ActiveRecord::Base.establish_connection(config)
|
15
|
+
ActiveRecord::Base.connection.create_domain(config['domain_name'])
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "model with active_record_simple_db" do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
ActiveRecord::Base.establish_connection($config)
|
7
|
+
ActiveRecord::Base.connection.create_domain($config[:domain_name])
|
8
|
+
end
|
9
|
+
|
10
|
+
after :each do
|
11
|
+
ActiveRecord::Base.connection.delete_domain($config[:domain_name])
|
12
|
+
end
|
13
|
+
#test .new
|
14
|
+
it "should be without id, created_at and updated_at when model is new" do
|
15
|
+
p = Person.new
|
16
|
+
p.id.should be_nil
|
17
|
+
p.created_at.should be_nil
|
18
|
+
p.updated_at.should be_nil
|
19
|
+
end
|
20
|
+
|
21
|
+
#test .create and .save
|
22
|
+
it "should be with id, created_at and updated_at when saved when new model was saved or created immediately" do
|
23
|
+
p1 = Person.new
|
24
|
+
p1.save
|
25
|
+
p1.id.should_not be_nil
|
26
|
+
p1.created_at.should_not be_nil
|
27
|
+
p1.updated_at.should_not be_nil
|
28
|
+
p2 = Person.create!
|
29
|
+
p2.id.should_not be_nil
|
30
|
+
p2.created_at.should_not be_nil
|
31
|
+
p2.updated_at.should_not be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
#test correct saving/loading properties
|
35
|
+
it "should correct save and load all properties" do
|
36
|
+
p = Person.create!(Person.valid_params)
|
37
|
+
|
38
|
+
r = Person.find p.id
|
39
|
+
Person.valid_params.each do |key, value|
|
40
|
+
r.try(key).should == value
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
#test validates instructions
|
45
|
+
it "should be correct work with validates instruction" do
|
46
|
+
class PersonWithValidates < Person
|
47
|
+
validates_presence_of :login
|
48
|
+
validates_uniqueness_of :login
|
49
|
+
end
|
50
|
+
|
51
|
+
lambda {
|
52
|
+
PersonWithValidates.create!
|
53
|
+
}.should raise_error(ActiveRecord::RecordInvalid, "Validation failed: Login can't be blank")
|
54
|
+
|
55
|
+
PersonWithValidates.create!(Person.valid_params)
|
56
|
+
|
57
|
+
lambda {
|
58
|
+
PersonWithValidates.create!(Person.valid_params)
|
59
|
+
}.should raise_error(ActiveRecord::RecordInvalid, "Validation failed: Login has already been taken")
|
60
|
+
end
|
61
|
+
|
62
|
+
#test .create
|
63
|
+
it "should be different ids for different model instances" do
|
64
|
+
p1 = Person.create!
|
65
|
+
p2 = Person.create!
|
66
|
+
p1.id.should_not == p2.id
|
67
|
+
end
|
68
|
+
|
69
|
+
#test .find
|
70
|
+
it "should be found entry by id" do
|
71
|
+
p1 = Person.create!
|
72
|
+
p2 = Person.find p1.id
|
73
|
+
p1.should == p2
|
74
|
+
end
|
75
|
+
|
76
|
+
#test .all
|
77
|
+
it "should return collection of models when invoke .all" do
|
78
|
+
p = Person.new
|
79
|
+
p.save
|
80
|
+
r = Person.all
|
81
|
+
r.should_not be_nil
|
82
|
+
r.should_not be_empty
|
83
|
+
end
|
84
|
+
|
85
|
+
#test .first
|
86
|
+
it "should return valid entry when invoke .first" do
|
87
|
+
p = Person.new
|
88
|
+
p.save
|
89
|
+
p1 = Person.first
|
90
|
+
p1.should_not be_nil
|
91
|
+
end
|
92
|
+
|
93
|
+
#test .first
|
94
|
+
it "should return valid entry when invoke .last" do
|
95
|
+
p = Person.new
|
96
|
+
p.save
|
97
|
+
p1 = Person.last
|
98
|
+
p1.should_not be_nil
|
99
|
+
end
|
100
|
+
|
101
|
+
#test .touch
|
102
|
+
it "should update updated_at property when invoke .touch" do
|
103
|
+
p1 = Person.new
|
104
|
+
p1.save
|
105
|
+
p2 = Person.find p1.id
|
106
|
+
p1.touch
|
107
|
+
p1.updated_at.should > p2.updated_at
|
108
|
+
end
|
109
|
+
|
110
|
+
#test .destroy
|
111
|
+
it "should be destroy entry when invoke destroy" do
|
112
|
+
p1 = Person.new
|
113
|
+
p1.save
|
114
|
+
id = p1.id
|
115
|
+
p1.destroy
|
116
|
+
p2 = Person.find_by_id id
|
117
|
+
p2.should be_nil
|
118
|
+
end
|
119
|
+
|
120
|
+
#test optimistic locking
|
121
|
+
it "should be throw exception when invoke destroy twice for same object" do
|
122
|
+
p1 = Person.new
|
123
|
+
p1.save
|
124
|
+
p2 = Person.find p1.id
|
125
|
+
p1.year = 2008
|
126
|
+
p1.save
|
127
|
+
lambda {p2.destroy}.should raise_error(ActiveRecord::StaleObjectError)
|
128
|
+
end
|
129
|
+
|
130
|
+
#test where selection
|
131
|
+
it "should be correct work with where statment with equalities" do
|
132
|
+
p1 = Person.new
|
133
|
+
p1.year = 2008
|
134
|
+
p1.save
|
135
|
+
p2 = Person.new
|
136
|
+
p2.year = 2010
|
137
|
+
p2.save
|
138
|
+
p = Person.where(:year => 2010).all
|
139
|
+
p.should_not be_empty
|
140
|
+
end
|
141
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
# Set up gems listed in the Gemfile.
|
3
|
+
gemfile = File.expand_path('../../Gemfile', __FILE__)
|
4
|
+
begin
|
5
|
+
ENV['BUNDLE_GEMFILE'] = gemfile
|
6
|
+
require 'bundler'
|
7
|
+
Bundler.setup
|
8
|
+
rescue Bundler::GemNotFound => e
|
9
|
+
STDERR.puts e.message
|
10
|
+
STDERR.puts "Try running `bundle install`."
|
11
|
+
exit!
|
12
|
+
end if File.exist?(gemfile)
|
13
|
+
|
14
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
15
|
+
require 'rspec'
|
16
|
+
require 'aws'
|
17
|
+
require 'active_record'
|
18
|
+
require 'active_record/connection_adapters/simpledb_adapter'
|
19
|
+
require 'arel/visitors/simpledb'
|
20
|
+
|
21
|
+
CONNECTION_PARAMS = {
|
22
|
+
:adapter => 'simpledb',
|
23
|
+
:access_key_id => "some_key",
|
24
|
+
:secret_access_key => "some_secret",
|
25
|
+
:domain_name => "test_domain",
|
26
|
+
:host => 'localhost',
|
27
|
+
:port => '8080',
|
28
|
+
:protocol => 'http'
|
29
|
+
}
|
30
|
+
|
31
|
+
$config = CONNECTION_PARAMS
|
32
|
+
|
33
|
+
#define stub model
|
34
|
+
class Person < ActiveRecord::Base
|
35
|
+
establish_connection $config
|
36
|
+
|
37
|
+
columns_definition do |t|
|
38
|
+
t.string :login
|
39
|
+
t.integer :year, :limit => 4
|
40
|
+
t.boolean :active
|
41
|
+
t.string :state
|
42
|
+
t.float :price
|
43
|
+
t.integer :lock_version
|
44
|
+
|
45
|
+
t.timestamps
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.valid_params
|
49
|
+
{
|
50
|
+
:login => "john",
|
51
|
+
:year => 2010,
|
52
|
+
:active => true,
|
53
|
+
:price => 10.04,
|
54
|
+
:state => 'paid'
|
55
|
+
}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
metadata
ADDED
@@ -0,0 +1,246 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activerecord-simpledb-adapter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Ilia Ablamonov
|
14
|
+
- Alex Gorkunov
|
15
|
+
- Cloud Castle Inc.
|
16
|
+
autorequire:
|
17
|
+
bindir: bin
|
18
|
+
cert_chain: []
|
19
|
+
|
20
|
+
date: 2011-01-14 00:00:00 +03:00
|
21
|
+
default_executable:
|
22
|
+
dependencies:
|
23
|
+
- !ruby/object:Gem::Dependency
|
24
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 2
|
32
|
+
- 3
|
33
|
+
- 0
|
34
|
+
version: 2.3.0
|
35
|
+
requirement: *id001
|
36
|
+
prerelease: false
|
37
|
+
type: :development
|
38
|
+
name: rspec
|
39
|
+
- !ruby/object:Gem::Dependency
|
40
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 23
|
46
|
+
segments:
|
47
|
+
- 1
|
48
|
+
- 0
|
49
|
+
- 0
|
50
|
+
version: 1.0.0
|
51
|
+
requirement: *id002
|
52
|
+
prerelease: false
|
53
|
+
type: :development
|
54
|
+
name: bundler
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 7
|
62
|
+
segments:
|
63
|
+
- 1
|
64
|
+
- 5
|
65
|
+
- 2
|
66
|
+
version: 1.5.2
|
67
|
+
requirement: *id003
|
68
|
+
prerelease: false
|
69
|
+
type: :development
|
70
|
+
name: jeweler
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
hash: 3
|
78
|
+
segments:
|
79
|
+
- 0
|
80
|
+
version: "0"
|
81
|
+
requirement: *id004
|
82
|
+
prerelease: false
|
83
|
+
type: :development
|
84
|
+
name: rcov
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
hash: 3
|
92
|
+
segments:
|
93
|
+
- 0
|
94
|
+
version: "0"
|
95
|
+
requirement: *id005
|
96
|
+
prerelease: false
|
97
|
+
type: :development
|
98
|
+
name: aws
|
99
|
+
- !ruby/object:Gem::Dependency
|
100
|
+
version_requirements: &id006 !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
hash: 3
|
106
|
+
segments:
|
107
|
+
- 0
|
108
|
+
version: "0"
|
109
|
+
requirement: *id006
|
110
|
+
prerelease: false
|
111
|
+
type: :development
|
112
|
+
name: activerecord
|
113
|
+
- !ruby/object:Gem::Dependency
|
114
|
+
version_requirements: &id007 !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
hash: 3
|
120
|
+
segments:
|
121
|
+
- 0
|
122
|
+
version: "0"
|
123
|
+
requirement: *id007
|
124
|
+
prerelease: false
|
125
|
+
type: :development
|
126
|
+
name: activesupport
|
127
|
+
- !ruby/object:Gem::Dependency
|
128
|
+
version_requirements: &id008 !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ~>
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
hash: 3
|
134
|
+
segments:
|
135
|
+
- 2
|
136
|
+
- 3
|
137
|
+
- 0
|
138
|
+
version: 2.3.0
|
139
|
+
requirement: *id008
|
140
|
+
prerelease: false
|
141
|
+
type: :runtime
|
142
|
+
name: aws
|
143
|
+
- !ruby/object:Gem::Dependency
|
144
|
+
version_requirements: &id009 !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ~>
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
hash: 1
|
150
|
+
segments:
|
151
|
+
- 3
|
152
|
+
- 0
|
153
|
+
- 3
|
154
|
+
version: 3.0.3
|
155
|
+
requirement: *id009
|
156
|
+
prerelease: false
|
157
|
+
type: :runtime
|
158
|
+
name: activerecord
|
159
|
+
- !ruby/object:Gem::Dependency
|
160
|
+
version_requirements: &id010 !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ~>
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
hash: 1
|
166
|
+
segments:
|
167
|
+
- 3
|
168
|
+
- 0
|
169
|
+
- 3
|
170
|
+
version: 3.0.3
|
171
|
+
requirement: *id010
|
172
|
+
prerelease: false
|
173
|
+
type: :runtime
|
174
|
+
name: activesupport
|
175
|
+
- !ruby/object:Gem::Dependency
|
176
|
+
version_requirements: &id011 !ruby/object:Gem::Requirement
|
177
|
+
none: false
|
178
|
+
requirements:
|
179
|
+
- - ~>
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
hash: 9
|
182
|
+
segments:
|
183
|
+
- 2
|
184
|
+
- 1
|
185
|
+
- 1
|
186
|
+
version: 2.1.1
|
187
|
+
requirement: *id011
|
188
|
+
prerelease: false
|
189
|
+
type: :runtime
|
190
|
+
name: uuidtools
|
191
|
+
description:
|
192
|
+
email: ilia@flamefork.ru
|
193
|
+
executables: []
|
194
|
+
|
195
|
+
extensions: []
|
196
|
+
|
197
|
+
extra_rdoc_files:
|
198
|
+
- LICENSE.txt
|
199
|
+
- README.md
|
200
|
+
files:
|
201
|
+
- LICENSE.txt
|
202
|
+
- README.md
|
203
|
+
- activerecord-simpledb-adapter.gemspec
|
204
|
+
- lib/active_record/connection_adapters/simpledb_adapter.rb
|
205
|
+
- lib/activerecord-simpledb-adapter.rb
|
206
|
+
- lib/arel/visitors/simpledb.rb
|
207
|
+
- lib/tasks/simpledb.rake
|
208
|
+
- spec/activerecord-simpledb-adapter_spec.rb
|
209
|
+
- spec/spec_helper.rb
|
210
|
+
has_rdoc: true
|
211
|
+
homepage: http://github.com/cloudcastle/activerecord-simpledb-adapter
|
212
|
+
licenses:
|
213
|
+
- MIT
|
214
|
+
post_install_message:
|
215
|
+
rdoc_options: []
|
216
|
+
|
217
|
+
require_paths:
|
218
|
+
- lib
|
219
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
220
|
+
none: false
|
221
|
+
requirements:
|
222
|
+
- - ">="
|
223
|
+
- !ruby/object:Gem::Version
|
224
|
+
hash: 3
|
225
|
+
segments:
|
226
|
+
- 0
|
227
|
+
version: "0"
|
228
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
229
|
+
none: false
|
230
|
+
requirements:
|
231
|
+
- - ">="
|
232
|
+
- !ruby/object:Gem::Version
|
233
|
+
hash: 3
|
234
|
+
segments:
|
235
|
+
- 0
|
236
|
+
version: "0"
|
237
|
+
requirements: []
|
238
|
+
|
239
|
+
rubyforge_project:
|
240
|
+
rubygems_version: 1.3.7
|
241
|
+
signing_key:
|
242
|
+
specification_version: 3
|
243
|
+
summary: ActiveRecord SimpleDB adapter
|
244
|
+
test_files:
|
245
|
+
- spec/activerecord-simpledb-adapter_spec.rb
|
246
|
+
- spec/spec_helper.rb
|