dynamoid 0.7.1 → 1.0.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.
- checksums.yaml +7 -0
- data/Gemfile +2 -24
- data/README.markdown +89 -73
- data/Rakefile +10 -36
- data/dynamoid.gemspec +56 -191
- data/lib/dynamoid.rb +6 -4
- data/lib/dynamoid/adapter.rb +64 -150
- data/lib/dynamoid/adapter_plugin/aws_sdk_v2.rb +579 -0
- data/lib/dynamoid/components.rb +0 -1
- data/lib/dynamoid/config.rb +2 -5
- data/lib/dynamoid/criteria.rb +1 -1
- data/lib/dynamoid/criteria/chain.rb +27 -140
- data/lib/dynamoid/document.rb +2 -2
- data/lib/dynamoid/errors.rb +30 -9
- data/lib/dynamoid/fields.rb +15 -3
- data/lib/dynamoid/finders.rb +7 -6
- data/lib/dynamoid/identity_map.rb +1 -5
- data/lib/dynamoid/persistence.rb +108 -93
- metadata +56 -229
- data/.document +0 -5
- data/.rspec +0 -1
- data/.travis.yml +0 -7
- data/Gemfile.lock +0 -81
- data/Gemfile_activemodel4 +0 -24
- data/Gemfile_activemodel4.lock +0 -88
- data/VERSION +0 -1
- data/doc/.nojekyll +0 -0
- data/doc/Dynamoid.html +0 -328
- data/doc/Dynamoid/Adapter.html +0 -1872
- data/doc/Dynamoid/Adapter/AwsSdk.html +0 -2101
- data/doc/Dynamoid/Adapter/Local.html +0 -1574
- data/doc/Dynamoid/Associations.html +0 -138
- data/doc/Dynamoid/Associations/Association.html +0 -847
- data/doc/Dynamoid/Associations/BelongsTo.html +0 -161
- data/doc/Dynamoid/Associations/ClassMethods.html +0 -766
- data/doc/Dynamoid/Associations/HasAndBelongsToMany.html +0 -167
- data/doc/Dynamoid/Associations/HasMany.html +0 -167
- data/doc/Dynamoid/Associations/HasOne.html +0 -161
- data/doc/Dynamoid/Associations/ManyAssociation.html +0 -1684
- data/doc/Dynamoid/Associations/SingleAssociation.html +0 -627
- data/doc/Dynamoid/Components.html +0 -242
- data/doc/Dynamoid/Config.html +0 -412
- data/doc/Dynamoid/Config/Options.html +0 -638
- data/doc/Dynamoid/Criteria.html +0 -138
- data/doc/Dynamoid/Criteria/Chain.html +0 -1471
- data/doc/Dynamoid/Criteria/ClassMethods.html +0 -105
- data/doc/Dynamoid/Dirty.html +0 -424
- data/doc/Dynamoid/Dirty/ClassMethods.html +0 -174
- data/doc/Dynamoid/Document.html +0 -1033
- data/doc/Dynamoid/Document/ClassMethods.html +0 -1116
- data/doc/Dynamoid/Errors.html +0 -125
- data/doc/Dynamoid/Errors/ConditionalCheckFailedException.html +0 -141
- data/doc/Dynamoid/Errors/DocumentNotValid.html +0 -221
- data/doc/Dynamoid/Errors/Error.html +0 -137
- data/doc/Dynamoid/Errors/InvalidField.html +0 -141
- data/doc/Dynamoid/Errors/InvalidQuery.html +0 -131
- data/doc/Dynamoid/Errors/MissingRangeKey.html +0 -141
- data/doc/Dynamoid/Fields.html +0 -686
- data/doc/Dynamoid/Fields/ClassMethods.html +0 -438
- data/doc/Dynamoid/Finders.html +0 -135
- data/doc/Dynamoid/Finders/ClassMethods.html +0 -943
- data/doc/Dynamoid/IdentityMap.html +0 -492
- data/doc/Dynamoid/IdentityMap/ClassMethods.html +0 -534
- data/doc/Dynamoid/Indexes.html +0 -321
- data/doc/Dynamoid/Indexes/ClassMethods.html +0 -369
- data/doc/Dynamoid/Indexes/Index.html +0 -1142
- data/doc/Dynamoid/Middleware.html +0 -115
- data/doc/Dynamoid/Middleware/IdentityMap.html +0 -264
- data/doc/Dynamoid/Persistence.html +0 -892
- data/doc/Dynamoid/Persistence/ClassMethods.html +0 -836
- data/doc/Dynamoid/Validations.html +0 -415
- data/doc/_index.html +0 -506
- data/doc/class_list.html +0 -53
- data/doc/css/common.css +0 -1
- data/doc/css/full_list.css +0 -57
- data/doc/css/style.css +0 -338
- data/doc/file.LICENSE.html +0 -73
- data/doc/file.README.html +0 -416
- data/doc/file_list.html +0 -58
- data/doc/frames.html +0 -28
- data/doc/index.html +0 -416
- data/doc/js/app.js +0 -214
- data/doc/js/full_list.js +0 -178
- data/doc/js/jquery.js +0 -4
- data/doc/method_list.html +0 -1144
- data/doc/top-level-namespace.html +0 -112
- data/lib/dynamoid/adapter/aws_sdk.rb +0 -287
- data/lib/dynamoid/indexes.rb +0 -69
- data/lib/dynamoid/indexes/index.rb +0 -103
- data/spec/app/models/address.rb +0 -13
- data/spec/app/models/camel_case.rb +0 -34
- data/spec/app/models/car.rb +0 -6
- data/spec/app/models/magazine.rb +0 -11
- data/spec/app/models/message.rb +0 -9
- data/spec/app/models/nuclear_submarine.rb +0 -5
- data/spec/app/models/sponsor.rb +0 -8
- data/spec/app/models/subscription.rb +0 -12
- data/spec/app/models/tweet.rb +0 -12
- data/spec/app/models/user.rb +0 -26
- data/spec/app/models/vehicle.rb +0 -7
- data/spec/dynamoid/adapter/aws_sdk_spec.rb +0 -376
- data/spec/dynamoid/adapter_spec.rb +0 -155
- data/spec/dynamoid/associations/association_spec.rb +0 -194
- data/spec/dynamoid/associations/belongs_to_spec.rb +0 -71
- data/spec/dynamoid/associations/has_and_belongs_to_many_spec.rb +0 -47
- data/spec/dynamoid/associations/has_many_spec.rb +0 -42
- data/spec/dynamoid/associations/has_one_spec.rb +0 -45
- data/spec/dynamoid/associations_spec.rb +0 -16
- data/spec/dynamoid/config_spec.rb +0 -27
- data/spec/dynamoid/criteria/chain_spec.rb +0 -210
- data/spec/dynamoid/criteria_spec.rb +0 -75
- data/spec/dynamoid/dirty_spec.rb +0 -57
- data/spec/dynamoid/document_spec.rb +0 -180
- data/spec/dynamoid/fields_spec.rb +0 -156
- data/spec/dynamoid/finders_spec.rb +0 -147
- data/spec/dynamoid/identity_map_spec.rb +0 -45
- data/spec/dynamoid/indexes/index_spec.rb +0 -104
- data/spec/dynamoid/indexes_spec.rb +0 -25
- data/spec/dynamoid/persistence_spec.rb +0 -301
- data/spec/dynamoid/validations_spec.rb +0 -36
- data/spec/dynamoid_spec.rb +0 -9
- data/spec/spec_helper.rb +0 -55
- data/spec/support/with_partitioning.rb +0 -15
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
module Dynamoid #:nodoc:
|
|
3
|
-
module Indexes
|
|
4
|
-
|
|
5
|
-
# The class contains all the information an index contains, including its keys and which attributes it covers.
|
|
6
|
-
class Index
|
|
7
|
-
attr_accessor :source, :name, :hash_keys, :range_keys
|
|
8
|
-
alias_method :range_key?, :range_keys
|
|
9
|
-
|
|
10
|
-
# Create a new index. Pass either :range => true or :range => :column_name to create a ranged index on that column.
|
|
11
|
-
#
|
|
12
|
-
# @param [Class] source the source class for the index
|
|
13
|
-
# @param [Symbol] name the name of the index
|
|
14
|
-
#
|
|
15
|
-
# @since 0.2.0
|
|
16
|
-
def initialize(source, name, options = {})
|
|
17
|
-
@source = source
|
|
18
|
-
|
|
19
|
-
if options.delete(:range)
|
|
20
|
-
@range_keys = sort(name)
|
|
21
|
-
elsif options[:range_key]
|
|
22
|
-
@range_keys = sort(options[:range_key])
|
|
23
|
-
end
|
|
24
|
-
@hash_keys = sort(name)
|
|
25
|
-
@name = sort([hash_keys, range_keys])
|
|
26
|
-
|
|
27
|
-
raise Dynamoid::Errors::InvalidField, 'A key specified for an index is not a field' unless keys.all?{|n| source.attributes.include?(n)}
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Sort objects into alphabetical strings, used for composing index names correctly (since we always assume they're alphabetical).
|
|
31
|
-
#
|
|
32
|
-
# @example find all users by first and last name
|
|
33
|
-
# sort([:gamma, :alpha, :beta, :omega]) # => [:alpha, :beta, :gamma, :omega]
|
|
34
|
-
#
|
|
35
|
-
# @since 0.2.0
|
|
36
|
-
def sort(objs)
|
|
37
|
-
Array(objs).flatten.compact.uniq.collect(&:to_s).sort.collect(&:to_sym)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Return the array of keys this index uses for its table.
|
|
41
|
-
#
|
|
42
|
-
# @since 0.2.0
|
|
43
|
-
def keys
|
|
44
|
-
[Array(hash_keys) + Array(range_keys)].flatten.uniq
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# Return the table name for this index.
|
|
48
|
-
#
|
|
49
|
-
# @since 0.2.0
|
|
50
|
-
def table_name
|
|
51
|
-
"#{Dynamoid::Config.namespace}_index_" + source.table_name.sub("#{Dynamoid::Config.namespace}_", '').singularize + "_#{name.collect(&:to_s).collect(&:pluralize).join('_and_')}"
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Given either an object or a list of attributes, generate a hash key and a range key for the index. Optionally pass in
|
|
55
|
-
# true to changed_attributes for a list of all the object's dirty attributes in convenient index form (for deleting stale
|
|
56
|
-
# information from the indexes).
|
|
57
|
-
#
|
|
58
|
-
# @param [Object] attrs either an object that responds to :attributes, or a hash of attributes
|
|
59
|
-
#
|
|
60
|
-
# @return [Hash] a hash with the keys :hash_value and :range_value
|
|
61
|
-
#
|
|
62
|
-
# @since 0.2.0
|
|
63
|
-
def values(attrs, changed_attributes = false)
|
|
64
|
-
if changed_attributes
|
|
65
|
-
hash = {}
|
|
66
|
-
attrs.changes.each {|k, v| hash[k.to_sym] = (v.first || v.last)}
|
|
67
|
-
attrs = hash
|
|
68
|
-
end
|
|
69
|
-
attrs = attrs.send(:attributes) if attrs.respond_to?(:attributes)
|
|
70
|
-
{}.tap do |hash|
|
|
71
|
-
hash[:hash_value] = hash_keys.collect{|key| attrs[key]}.join('.')
|
|
72
|
-
hash[:range_value] = range_keys.inject(0.0) {|sum, key| sum + attrs[key].to_f} if self.range_key?
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# Save an object to this index, merging it with existing ids if there's already something present at this index location.
|
|
77
|
-
# First, though, delete this object from its old indexes (so the object isn't listed in an erroneous index).
|
|
78
|
-
#
|
|
79
|
-
# @since 0.2.0
|
|
80
|
-
def save(obj)
|
|
81
|
-
self.delete(obj, true)
|
|
82
|
-
values = values(obj)
|
|
83
|
-
return true if values[:hash_value].blank? || (!values[:range_value].nil? && values[:range_value].blank?)
|
|
84
|
-
existing = Dynamoid::Adapter.read(self.table_name, values[:hash_value], { :range_key => values[:range_value] })
|
|
85
|
-
ids = ((existing and existing[:ids]) or Set.new)
|
|
86
|
-
Dynamoid::Adapter.write(self.table_name, {:id => values[:hash_value], :ids => ids.merge([obj.id]), :range => values[:range_value]})
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# Delete an object from this index, preserving existing ids if there are any, and failing gracefully if for some reason the
|
|
90
|
-
# index doesn't already have this object in it.
|
|
91
|
-
#
|
|
92
|
-
# @since 0.2.0
|
|
93
|
-
def delete(obj, changed_attributes = false)
|
|
94
|
-
values = values(obj, changed_attributes)
|
|
95
|
-
return true if values[:hash_value].blank? || (!values[:range_value].nil? && values[:range_value].blank?)
|
|
96
|
-
existing = Dynamoid::Adapter.read(self.table_name, values[:hash_value], { :range_key => values[:range_value]})
|
|
97
|
-
return true unless existing && existing[:ids] && existing[:ids].include?(obj.id)
|
|
98
|
-
Dynamoid::Adapter.write(self.table_name, {:id => values[:hash_value], :ids => (existing[:ids] - Set[obj.id]), :range => values[:range_value]})
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
end
|
data/spec/app/models/address.rb
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
class CamelCase
|
|
2
|
-
include Dynamoid::Document
|
|
3
|
-
|
|
4
|
-
field :color
|
|
5
|
-
|
|
6
|
-
belongs_to :magazine
|
|
7
|
-
has_many :users
|
|
8
|
-
has_one :sponsor
|
|
9
|
-
has_and_belongs_to_many :subscriptions
|
|
10
|
-
|
|
11
|
-
before_create :doing_before_create
|
|
12
|
-
after_create :doing_after_create
|
|
13
|
-
before_update :doing_before_update
|
|
14
|
-
after_update :doing_after_update
|
|
15
|
-
|
|
16
|
-
private
|
|
17
|
-
|
|
18
|
-
def doing_before_create
|
|
19
|
-
true
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def doing_after_create
|
|
23
|
-
true
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def doing_before_update
|
|
27
|
-
true
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def doing_after_update
|
|
31
|
-
true
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
end
|
data/spec/app/models/car.rb
DELETED
data/spec/app/models/magazine.rb
DELETED
data/spec/app/models/message.rb
DELETED
data/spec/app/models/sponsor.rb
DELETED
data/spec/app/models/tweet.rb
DELETED
data/spec/app/models/user.rb
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
class User
|
|
2
|
-
include Dynamoid::Document
|
|
3
|
-
|
|
4
|
-
field :name
|
|
5
|
-
field :email
|
|
6
|
-
field :password
|
|
7
|
-
field :last_logged_in_at, :datetime
|
|
8
|
-
|
|
9
|
-
index :name
|
|
10
|
-
index :email
|
|
11
|
-
index [:name, :email]
|
|
12
|
-
index :name, :range_key => :created_at
|
|
13
|
-
index :name, :range_key => :last_logged_in_at
|
|
14
|
-
index :created_at, :range => true
|
|
15
|
-
|
|
16
|
-
has_and_belongs_to_many :subscriptions
|
|
17
|
-
|
|
18
|
-
has_many :books, :class_name => 'Magazine', :inverse_of => :owner
|
|
19
|
-
has_one :monthly, :class_name => 'Subscription', :inverse_of => :customer
|
|
20
|
-
|
|
21
|
-
has_and_belongs_to_many :followers, :class_name => 'User', :inverse_of => :following
|
|
22
|
-
has_and_belongs_to_many :following, :class_name => 'User', :inverse_of => :followers
|
|
23
|
-
|
|
24
|
-
belongs_to :camel_case
|
|
25
|
-
|
|
26
|
-
end
|
data/spec/app/models/vehicle.rb
DELETED
|
@@ -1,376 +0,0 @@
|
|
|
1
|
-
require 'dynamoid/adapter/aws_sdk'
|
|
2
|
-
require File.expand_path(File.dirname(__FILE__) + '../../../spec_helper')
|
|
3
|
-
|
|
4
|
-
describe Dynamoid::Adapter::AwsSdk do
|
|
5
|
-
before(:each) do
|
|
6
|
-
pending "You must have an active DynamoDB connection" unless ENV['ACCESS_KEY'] && ENV['SECRET_KEY']
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
#
|
|
10
|
-
# These let() definitions create tables "dynamoid_tests_TestTable<N>" and return the
|
|
11
|
-
# name of the table. An after(:each) handler drops any tables created this way after each test
|
|
12
|
-
#
|
|
13
|
-
# Name => Constructor args
|
|
14
|
-
{
|
|
15
|
-
1 => [:id],
|
|
16
|
-
2 => [:id],
|
|
17
|
-
3 => [:id, {:range_key => {:range => :number}}],
|
|
18
|
-
4 => [:id, {:range_key => {:range => :number}}]
|
|
19
|
-
}.each do |n, args|
|
|
20
|
-
name = "dynamoid_tests_TestTable#{n}"
|
|
21
|
-
let(:"test_table#{n}") do
|
|
22
|
-
Dynamoid::Adapter.create_table(name, *args)
|
|
23
|
-
@created_tables << name
|
|
24
|
-
name
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
before(:each) { @created_tables = [] }
|
|
29
|
-
after(:each) do
|
|
30
|
-
@created_tables.each do |t|
|
|
31
|
-
Dynamoid::Adapter.delete_table(t)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
#
|
|
36
|
-
# Returns a random key parition if partitioning is on, or an empty string if
|
|
37
|
-
# it is off, useful for shared examples where partitioning may or may not be on
|
|
38
|
-
#
|
|
39
|
-
def key_partition
|
|
40
|
-
Dynamoid::Config.partitioning? ? ".#{Random.rand(Dynamoid::Config.partition_size)}" : ''
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
#
|
|
44
|
-
# Tests adapter against ranged tables
|
|
45
|
-
#
|
|
46
|
-
shared_examples 'range queries' do
|
|
47
|
-
before do
|
|
48
|
-
Dynamoid::Adapter.put_item(test_table3, {:id => "1#{key_partition}", :range => 1.0})
|
|
49
|
-
Dynamoid::Adapter.put_item(test_table3, {:id => "1#{key_partition}", :range => 3.0})
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
it 'performs query on a table with a range and selects items in a range' do
|
|
53
|
-
Dynamoid::Adapter.query(test_table3, :hash_value => '1', :range_value => 0.0..3.0).to_a.should =~ [{:id => '1', :range => BigDecimal.new(1)}, {:id => '1', :range => BigDecimal.new(3)}]
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
it 'performs query on a table with a range and selects items in a range with :select option' do
|
|
57
|
-
Dynamoid::Adapter.query(test_table3, :hash_value => '1', :range_value => 0.0..3.0, :select => :all).to_a.should =~ [{:id => '1', :range => BigDecimal.new(1)}, {:id => '1', :range => BigDecimal.new(3)}]
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
it 'performs query on a table with a range and selects items greater than' do
|
|
61
|
-
Dynamoid::Adapter.query(test_table3, :hash_value => '1', :range_greater_than => 1.0).to_a.should =~ [{:id => '1', :range => BigDecimal.new(3)}]
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
it 'performs query on a table with a range and selects items less than' do
|
|
65
|
-
Dynamoid::Adapter.query(test_table3, :hash_value => '1', :range_less_than => 2.0).to_a.should =~ [{:id => '1', :range => BigDecimal.new(1)}]
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
it 'performs query on a table with a range and selects items gte' do
|
|
69
|
-
Dynamoid::Adapter.query(test_table3, :hash_value => '1', :range_gte => 1.0).to_a.should =~ [{:id => '1', :range => BigDecimal.new(1)}, {:id => '1', :range => BigDecimal.new(3)}]
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
it 'performs query on a table with a range and selects items lte' do
|
|
73
|
-
Dynamoid::Adapter.query(test_table3, :hash_value => '1', :range_lte => 3.0).to_a.should =~ [{:id => '1', :range => BigDecimal.new(1)}, {:id => '1', :range => BigDecimal.new(3)}]
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
#
|
|
78
|
-
# Tests scan_index_forwards flag behavior on range queries
|
|
79
|
-
#
|
|
80
|
-
shared_examples 'correct ordering' do
|
|
81
|
-
before(:each) do
|
|
82
|
-
pending "Order is not preserved on paritioned tables" if(Dynamoid::Config.partitioning?)
|
|
83
|
-
Dynamoid::Adapter.put_item(test_table4, {:id => "1#{key_partition}", :order => 1, :range => 1.0})
|
|
84
|
-
Dynamoid::Adapter.put_item(test_table4, {:id => "1#{key_partition}", :order => 2, :range => 2.0})
|
|
85
|
-
Dynamoid::Adapter.put_item(test_table4, {:id => "1#{key_partition}", :order => 3, :range => 3.0})
|
|
86
|
-
Dynamoid::Adapter.put_item(test_table4, {:id => "1#{key_partition}", :order => 4, :range => 4.0})
|
|
87
|
-
Dynamoid::Adapter.put_item(test_table4, {:id => "1#{key_partition}", :order => 5, :range => 5.0})
|
|
88
|
-
Dynamoid::Adapter.put_item(test_table4, {:id => "1#{key_partition}", :order => 6, :range => 6.0})
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
it 'performs query on a table with a range and selects items less than that is in the correct order, scan_index_forward true' do
|
|
92
|
-
query = Dynamoid::Adapter.query(test_table4, :hash_value => '1', :range_greater_than => 0, :scan_index_forward => true).to_a
|
|
93
|
-
query[0].should == {:id => '1', :order => 1, :range => BigDecimal.new(1)}
|
|
94
|
-
query[1].should == {:id => '1', :order => 2, :range => BigDecimal.new(2)}
|
|
95
|
-
query[2].should == {:id => '1', :order => 3, :range => BigDecimal.new(3)}
|
|
96
|
-
query[3].should == {:id => '1', :order => 4, :range => BigDecimal.new(4)}
|
|
97
|
-
query[4].should == {:id => '1', :order => 5, :range => BigDecimal.new(5)}
|
|
98
|
-
query[5].should == {:id => '1', :order => 6, :range => BigDecimal.new(6)}
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
it 'performs query on a table with a range and selects items less than that is in the correct order, scan_index_forward false' do
|
|
102
|
-
query = Dynamoid::Adapter.query(test_table4, :hash_value => '1', :range_greater_than => 0, :scan_index_forward => false).to_a
|
|
103
|
-
query[5].should == {:id => '1', :order => 1, :range => BigDecimal.new(1)}
|
|
104
|
-
query[4].should == {:id => '1', :order => 2, :range => BigDecimal.new(2)}
|
|
105
|
-
query[3].should == {:id => '1', :order => 3, :range => BigDecimal.new(3)}
|
|
106
|
-
query[2].should == {:id => '1', :order => 4, :range => BigDecimal.new(4)}
|
|
107
|
-
query[1].should == {:id => '1', :order => 5, :range => BigDecimal.new(5)}
|
|
108
|
-
query[0].should == {:id => '1', :order => 6, :range => BigDecimal.new(6)}
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
context 'without a preexisting table' do
|
|
114
|
-
# CreateTable and DeleteTable
|
|
115
|
-
it 'performs CreateTable and DeleteTable' do
|
|
116
|
-
table = Dynamoid::Adapter.create_table('CreateTable', :id, :range_key => { :created_at => :number })
|
|
117
|
-
|
|
118
|
-
Dynamoid::Adapter.connection.tables.collect{|t| t.name}.should include 'CreateTable'
|
|
119
|
-
|
|
120
|
-
Dynamoid::Adapter.delete_table('CreateTable')
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
context 'with a preexisting table without paritioning' do
|
|
126
|
-
# GetItem, PutItem and DeleteItem
|
|
127
|
-
it "performs GetItem for an item that does not exist" do
|
|
128
|
-
Dynamoid::Adapter.get_item(test_table1, '1').should be_nil
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
it "performs GetItem for an item that does exist" do
|
|
132
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
|
|
133
|
-
|
|
134
|
-
Dynamoid::Adapter.get_item(test_table1, '1').should == {:name => 'Josh', :id => '1'}
|
|
135
|
-
|
|
136
|
-
Dynamoid::Adapter.delete_item(test_table1, '1')
|
|
137
|
-
|
|
138
|
-
Dynamoid::Adapter.get_item(test_table1, '1').should be_nil
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
it 'performs GetItem for an item that does exist with a range key' do
|
|
142
|
-
Dynamoid::Adapter.put_item(test_table3, {:id => '1', :name => 'Josh', :range => 2.0})
|
|
143
|
-
|
|
144
|
-
Dynamoid::Adapter.get_item(test_table3, '1', :range_key => 2.0).should == {:name => 'Josh', :id => '1', :range => 2.0}
|
|
145
|
-
|
|
146
|
-
Dynamoid::Adapter.delete_item(test_table3, '1', :range_key => 2.0)
|
|
147
|
-
|
|
148
|
-
Dynamoid::Adapter.get_item(test_table3, '1', :range_key => 2.0).should be_nil
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
it 'performs DeleteItem for an item that does not exist' do
|
|
152
|
-
Dynamoid::Adapter.delete_item(test_table1, '1')
|
|
153
|
-
|
|
154
|
-
Dynamoid::Adapter.get_item(test_table1, '1').should be_nil
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
it 'performs PutItem for an item that does not exist' do
|
|
158
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
|
|
159
|
-
|
|
160
|
-
Dynamoid::Adapter.get_item(test_table1, '1').should == {:id => '1', :name => 'Josh'}
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
# BatchGetItem
|
|
164
|
-
it 'passes options to underlying BatchGet call' do
|
|
165
|
-
AWS::DynamoDB::BatchGet.any_instance.expects(:table).with(test_table1, :all, ['1', '2'], :consistent_read => true)
|
|
166
|
-
described_class.batch_get_item({test_table1 => ['1', '2']}, :consistent_read => true)
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
it "performs BatchGetItem with singular keys" do
|
|
170
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
|
|
171
|
-
Dynamoid::Adapter.put_item(test_table2, {:id => '1', :name => 'Justin'})
|
|
172
|
-
|
|
173
|
-
results = Dynamoid::Adapter.batch_get_item(test_table1 => '1', test_table2 => '1')
|
|
174
|
-
results.size.should == 2
|
|
175
|
-
results[test_table1].should include({:name => 'Josh', :id => '1'})
|
|
176
|
-
results[test_table2].should include({:name => 'Justin', :id => '1'})
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
it "performs BatchGetItem with multiple keys" do
|
|
180
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
|
|
181
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '2', :name => 'Justin'})
|
|
182
|
-
|
|
183
|
-
results = Dynamoid::Adapter.batch_get_item(test_table1 => ['1', '2'])
|
|
184
|
-
results.size.should == 1
|
|
185
|
-
results[test_table1].should include({:name => 'Josh', :id => '1'})
|
|
186
|
-
results[test_table1].should include({:name => 'Justin', :id => '2'})
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
it 'performs BatchGetItem with one ranged key' do
|
|
190
|
-
Dynamoid::Adapter.put_item(test_table3, {:id => '1', :name => 'Josh', :range => 1.0})
|
|
191
|
-
Dynamoid::Adapter.put_item(test_table3, {:id => '2', :name => 'Justin', :range => 2.0})
|
|
192
|
-
|
|
193
|
-
results = Dynamoid::Adapter.batch_get_item(test_table3 => [['1', 1.0]])
|
|
194
|
-
results.size.should == 1
|
|
195
|
-
results[test_table3].should include({:name => 'Josh', :id => '1', :range => 1.0})
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
it 'performs BatchGetItem with multiple ranged keys' do
|
|
199
|
-
Dynamoid::Adapter.put_item(test_table3, {:id => '1', :name => 'Josh', :range => 1.0})
|
|
200
|
-
Dynamoid::Adapter.put_item(test_table3, {:id => '2', :name => 'Justin', :range => 2.0})
|
|
201
|
-
|
|
202
|
-
results = Dynamoid::Adapter.batch_get_item(test_table3 => [['1', 1.0],['2', 2.0]])
|
|
203
|
-
results.size.should == 1
|
|
204
|
-
results[test_table3].should include({:name => 'Josh', :id => '1', :range => 1.0})
|
|
205
|
-
results[test_table3].should include({:name => 'Justin', :id => '2', :range => 2.0})
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
# BatchDeleteItem
|
|
209
|
-
it "performs BatchDeleteItem with singular keys" do
|
|
210
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
|
|
211
|
-
Dynamoid::Adapter.put_item(test_table2, {:id => '1', :name => 'Justin'})
|
|
212
|
-
|
|
213
|
-
Dynamoid::Adapter.batch_delete_item(test_table1 => ['1'], test_table2 => ['1'])
|
|
214
|
-
|
|
215
|
-
results = Dynamoid::Adapter.batch_get_item(test_table1 => '1', test_table2 => '1')
|
|
216
|
-
results.size.should == 0
|
|
217
|
-
|
|
218
|
-
results[test_table1].should_not include({:name => 'Josh', :id => '1'})
|
|
219
|
-
results[test_table2].should_not include({:name => 'Justin', :id => '1'})
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
it "performs BatchDeleteItem with multiple keys" do
|
|
223
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
|
|
224
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '2', :name => 'Justin'})
|
|
225
|
-
|
|
226
|
-
Dynamoid::Adapter.batch_delete_item(test_table1 => ['1', '2'])
|
|
227
|
-
|
|
228
|
-
results = Dynamoid::Adapter.batch_get_item(test_table1 => ['1', '2'])
|
|
229
|
-
results.size.should == 0
|
|
230
|
-
|
|
231
|
-
results[test_table1].should_not include({:name => 'Josh', :id => '1'})
|
|
232
|
-
results[test_table1].should_not include({:name => 'Justin', :id => '2'})
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
it 'performs BatchDeleteItem with one ranged key' do
|
|
236
|
-
Dynamoid::Adapter.put_item(test_table3, {:id => '1', :name => 'Josh', :range => 1.0})
|
|
237
|
-
Dynamoid::Adapter.put_item(test_table3, {:id => '2', :name => 'Justin', :range => 2.0})
|
|
238
|
-
|
|
239
|
-
Dynamoid::Adapter.batch_delete_item(test_table3 => [['1', 1.0]])
|
|
240
|
-
results = Dynamoid::Adapter.batch_get_item(test_table3 => [['1', 1.0]])
|
|
241
|
-
results.size.should == 0
|
|
242
|
-
|
|
243
|
-
results[test_table3].should_not include({:name => 'Josh', :id => '1', :range => 1.0})
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
it 'performs BatchDeleteItem with multiple ranged keys' do
|
|
247
|
-
Dynamoid::Adapter.put_item(test_table3, {:id => '1', :name => 'Josh', :range => 1.0})
|
|
248
|
-
Dynamoid::Adapter.put_item(test_table3, {:id => '2', :name => 'Justin', :range => 2.0})
|
|
249
|
-
|
|
250
|
-
Dynamoid::Adapter.batch_delete_item(test_table3 => [['1', 1.0],['2', 2.0]])
|
|
251
|
-
results = Dynamoid::Adapter.batch_get_item(test_table3 => [['1', 1.0],['2', 2.0]])
|
|
252
|
-
results.size.should == 0
|
|
253
|
-
|
|
254
|
-
results[test_table3].should_not include({:name => 'Josh', :id => '1', :range => 1.0})
|
|
255
|
-
results[test_table3].should_not include({:name => 'Justin', :id => '2', :range => 2.0})
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
# ListTables
|
|
259
|
-
it 'performs ListTables' do
|
|
260
|
-
#Force creation of the tables
|
|
261
|
-
test_table1; test_table2; test_table3; test_table4
|
|
262
|
-
|
|
263
|
-
Dynamoid::Adapter.list_tables.should include test_table1
|
|
264
|
-
Dynamoid::Adapter.list_tables.should include test_table2
|
|
265
|
-
end
|
|
266
|
-
|
|
267
|
-
# Query
|
|
268
|
-
it 'performs query on a table and returns items' do
|
|
269
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
|
|
270
|
-
|
|
271
|
-
Dynamoid::Adapter.query(test_table1, :hash_value => '1').first.should == { :id=> '1', :name=>"Josh" }
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
it 'performs query on a table and returns items if there are multiple items' do
|
|
275
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
|
|
276
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '2', :name => 'Justin'})
|
|
277
|
-
|
|
278
|
-
Dynamoid::Adapter.query(test_table1, :hash_value => '1').first.should == { :id=> '1', :name=>"Josh" }
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
it_behaves_like 'range queries'
|
|
282
|
-
|
|
283
|
-
# Scan
|
|
284
|
-
it 'performs scan on a table and returns items' do
|
|
285
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
|
|
286
|
-
|
|
287
|
-
Dynamoid::Adapter.scan(test_table1, :name => 'Josh').to_a.should == [{ :id=> '1', :name=>"Josh" }]
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
it 'performs scan on a table and returns items if there are multiple items but only one match' do
|
|
291
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
|
|
292
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '2', :name => 'Justin'})
|
|
293
|
-
|
|
294
|
-
Dynamoid::Adapter.scan(test_table1, :name => 'Josh').to_a.should == [{ :id=> '1', :name=>"Josh" }]
|
|
295
|
-
end
|
|
296
|
-
|
|
297
|
-
it 'performs scan on a table and returns multiple items if there are multiple matches' do
|
|
298
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
|
|
299
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '2', :name => 'Josh'})
|
|
300
|
-
|
|
301
|
-
Dynamoid::Adapter.scan(test_table1, :name => 'Josh').should include({:name=>"Josh", :id=>"2"}, {:name=>"Josh", :id=>"1"})
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
it 'performs scan on a table and returns all items if no criteria are specified' do
|
|
305
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
|
|
306
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '2', :name => 'Josh'})
|
|
307
|
-
|
|
308
|
-
Dynamoid::Adapter.scan(test_table1, {}).should include({:name=>"Josh", :id=>"2"}, {:name=>"Josh", :id=>"1"})
|
|
309
|
-
end
|
|
310
|
-
|
|
311
|
-
it_behaves_like 'correct ordering'
|
|
312
|
-
end
|
|
313
|
-
|
|
314
|
-
context 'with a preexisting table with paritioning' do
|
|
315
|
-
before(:all) do
|
|
316
|
-
@previous_value = Dynamoid::Config.partitioning
|
|
317
|
-
Dynamoid::Config.partitioning = true
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
after(:all) do
|
|
321
|
-
Dynamoid::Config.partitioning = @previous_value
|
|
322
|
-
end
|
|
323
|
-
|
|
324
|
-
# Query
|
|
325
|
-
it 'performs query on a table and returns items' do
|
|
326
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '1.1', :name => 'Josh'})
|
|
327
|
-
|
|
328
|
-
Dynamoid::Adapter.query(test_table1, :hash_value => '1').first.should == { :id=> '1', :name=>"Josh" }
|
|
329
|
-
end
|
|
330
|
-
|
|
331
|
-
it 'performs query on a table and returns items if there are multiple items' do
|
|
332
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '1.1', :name => 'Josh'})
|
|
333
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '2.1', :name => 'Justin'})
|
|
334
|
-
|
|
335
|
-
Dynamoid::Adapter.query(test_table1, :hash_value => '1').first.should == { :id=> '1', :name=>"Josh" }
|
|
336
|
-
end
|
|
337
|
-
|
|
338
|
-
it_behaves_like 'range queries'
|
|
339
|
-
|
|
340
|
-
# Scan
|
|
341
|
-
it 'performs scan on a table and returns items' do
|
|
342
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '1.1', :name => 'Josh'})
|
|
343
|
-
|
|
344
|
-
Dynamoid::Adapter.scan(test_table1, :name => 'Josh').should == [{ :id=> '1', :name=>"Josh" }]
|
|
345
|
-
end
|
|
346
|
-
|
|
347
|
-
it 'performs scan on a table and returns items if there are multiple items but only one match' do
|
|
348
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '1.1', :name => 'Josh'})
|
|
349
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '2.1', :name => 'Justin'})
|
|
350
|
-
|
|
351
|
-
Dynamoid::Adapter.scan(test_table1, :name => 'Josh').should == [{ :id=> '1', :name=>"Josh" }]
|
|
352
|
-
end
|
|
353
|
-
|
|
354
|
-
it 'performs scan on a table and returns multiple items if there are multiple matches' do
|
|
355
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '1.1', :name => 'Josh'})
|
|
356
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '2.1', :name => 'Josh'})
|
|
357
|
-
|
|
358
|
-
Dynamoid::Adapter.scan(test_table1, :name => 'Josh').should include({:name=>"Josh", :id=>"2"}, {:name=>"Josh", :id=>"1"})
|
|
359
|
-
end
|
|
360
|
-
|
|
361
|
-
it 'performs scan on a table and returns all items if no criteria are specified' do
|
|
362
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '1.1', :name => 'Josh'})
|
|
363
|
-
Dynamoid::Adapter.put_item(test_table1, {:id => '2.1', :name => 'Josh'})
|
|
364
|
-
|
|
365
|
-
Dynamoid::Adapter.scan(test_table1, {}).should include({:name=>"Josh", :id=>"2"}, {:name=>"Josh", :id=>"1"})
|
|
366
|
-
end
|
|
367
|
-
|
|
368
|
-
it_behaves_like 'correct ordering'
|
|
369
|
-
end
|
|
370
|
-
|
|
371
|
-
# DescribeTable
|
|
372
|
-
|
|
373
|
-
# UpdateItem
|
|
374
|
-
|
|
375
|
-
# UpdateTable
|
|
376
|
-
end
|