dynamoid 0.0.2
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/.document +5 -0
- data/.rspec +1 -0
- data/Dynamoid.gemspec +116 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +58 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +86 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/lib/dynamoid.rb +32 -0
- data/lib/dynamoid/adapter.rb +24 -0
- data/lib/dynamoid/adapter/aws_sdk.rb +100 -0
- data/lib/dynamoid/adapter/local.rb +77 -0
- data/lib/dynamoid/associations.rb +54 -0
- data/lib/dynamoid/associations/association.rb +80 -0
- data/lib/dynamoid/associations/belongs_to.rb +39 -0
- data/lib/dynamoid/associations/has_and_belongs_to_many.rb +34 -0
- data/lib/dynamoid/associations/has_many.rb +33 -0
- data/lib/dynamoid/associations/has_one.rb +36 -0
- data/lib/dynamoid/attributes.rb +37 -0
- data/lib/dynamoid/components.rb +25 -0
- data/lib/dynamoid/config.rb +19 -0
- data/lib/dynamoid/config/options.rb +74 -0
- data/lib/dynamoid/document.rb +35 -0
- data/lib/dynamoid/errors.rb +8 -0
- data/lib/dynamoid/fields.rb +32 -0
- data/lib/dynamoid/finders.rb +62 -0
- data/lib/dynamoid/indexes.rb +59 -0
- data/lib/dynamoid/persistence.rb +35 -0
- data/lib/dynamoid/relations.rb +21 -0
- data/spec/app/models/address.rb +5 -0
- data/spec/app/models/magazine.rb +6 -0
- data/spec/app/models/sponsor.rb +6 -0
- data/spec/app/models/subscription.rb +6 -0
- data/spec/app/models/user.rb +13 -0
- data/spec/dynamoid/adapter/aws_sdk_spec.rb +123 -0
- data/spec/dynamoid/adapter/local_spec.rb +150 -0
- data/spec/dynamoid/adapter_spec.rb +13 -0
- data/spec/dynamoid/associations/association_spec.rb +71 -0
- data/spec/dynamoid/associations/belongs_to_spec.rb +50 -0
- data/spec/dynamoid/associations/has_and_belongs_to_many_spec.rb +30 -0
- data/spec/dynamoid/associations/has_many_spec.rb +28 -0
- data/spec/dynamoid/associations/has_one_spec.rb +37 -0
- data/spec/dynamoid/associations_spec.rb +16 -0
- data/spec/dynamoid/attributes_spec.rb +43 -0
- data/spec/dynamoid/document_spec.rb +38 -0
- data/spec/dynamoid/fields_spec.rb +26 -0
- data/spec/dynamoid/finders_spec.rb +114 -0
- data/spec/dynamoid/indexes_spec.rb +54 -0
- data/spec/dynamoid/persistence_spec.rb +55 -0
- data/spec/dynamoid/relations_spec.rb +6 -0
- data/spec/dynamoid_spec.rb +5 -0
- data/spec/spec_helper.rb +52 -0
- metadata +204 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
module Dynamoid
|
|
2
|
+
module Adapter
|
|
3
|
+
module Local
|
|
4
|
+
extend self
|
|
5
|
+
# Gimpy hash that should be somewhat equivalent to what Amazon's actual DynamoDB, for offline development.
|
|
6
|
+
|
|
7
|
+
def data
|
|
8
|
+
@data ||= {}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def reset_data
|
|
12
|
+
self.data.each {|k, v| v[:data] = {}}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# BatchGetItem
|
|
16
|
+
def batch_get_item(options)
|
|
17
|
+
Hash.new { |h, k| h[k] = Array.new }.tap do |hash|
|
|
18
|
+
options.each do |table_name, keys|
|
|
19
|
+
table = data[table_name]
|
|
20
|
+
Array(keys).each do |key|
|
|
21
|
+
hash[table_name] << table[:data][key]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# CreateTable
|
|
28
|
+
def create_table(table_name, key)
|
|
29
|
+
data[table_name] = {:id => key, :data => {}}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# DeleteItem
|
|
33
|
+
def delete_item(table_name, key)
|
|
34
|
+
data[table_name][:data].delete(key)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# DeleteTable
|
|
38
|
+
def delete_table(table_name)
|
|
39
|
+
data.delete(table_name)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# DescribeTable
|
|
43
|
+
|
|
44
|
+
# GetItem
|
|
45
|
+
def get_item(table_name, key)
|
|
46
|
+
data[table_name][:data][key]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# ListTables
|
|
50
|
+
def list_tables
|
|
51
|
+
data.keys
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# PutItem
|
|
55
|
+
def put_item(table_name, object)
|
|
56
|
+
table = data[table_name]
|
|
57
|
+
table[:data][object[table[:id]]] = object
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Query
|
|
61
|
+
def query(table_name, id)
|
|
62
|
+
get_item(table_name, id)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Scan
|
|
66
|
+
def scan(table_name, scan_hash)
|
|
67
|
+
return [] if data[table_name].nil?
|
|
68
|
+
data[table_name][:data].values.select{|d| scan_hash.all?{|k, v| !d[k].nil? && d[k] == v}}
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# UpdateItem
|
|
72
|
+
|
|
73
|
+
# UpdateTable
|
|
74
|
+
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require 'dynamoid/associations/association'
|
|
2
|
+
require 'dynamoid/associations/has_many'
|
|
3
|
+
require 'dynamoid/associations/belongs_to'
|
|
4
|
+
require 'dynamoid/associations/has_one'
|
|
5
|
+
require 'dynamoid/associations/has_and_belongs_to_many'
|
|
6
|
+
|
|
7
|
+
# encoding: utf-8
|
|
8
|
+
module Dynamoid #:nodoc:
|
|
9
|
+
|
|
10
|
+
# Connects models together through the magic of associations.
|
|
11
|
+
module Associations
|
|
12
|
+
extend ActiveSupport::Concern
|
|
13
|
+
|
|
14
|
+
included do
|
|
15
|
+
class_attribute :associations
|
|
16
|
+
|
|
17
|
+
self.associations = {}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
module ClassMethods
|
|
21
|
+
def has_many(name, options = {})
|
|
22
|
+
association(:has_many, name, options)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def has_one(name, options = {})
|
|
26
|
+
association(:has_one, name, options)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def belongs_to(name, options = {})
|
|
30
|
+
association(:belongs_to, name, options)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def has_and_belongs_to_many(name, options = {})
|
|
34
|
+
association(:has_and_belongs_to_many, name, options)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def association(type, name, options = {})
|
|
40
|
+
field "#{name}_ids".to_sym
|
|
41
|
+
self.associations[name] = options.merge(:type => type)
|
|
42
|
+
define_method(name) do
|
|
43
|
+
@associations ||= {}
|
|
44
|
+
@associations[name] ||= Dynamoid::Associations.const_get(type.to_s.camelcase).new(self, name, options)
|
|
45
|
+
end
|
|
46
|
+
define_method("#{name}=".to_sym) do |objects|
|
|
47
|
+
self.send(name) << objects
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Dynamoid #:nodoc:
|
|
3
|
+
|
|
4
|
+
# The base association module.
|
|
5
|
+
module Associations
|
|
6
|
+
module Association
|
|
7
|
+
attr_accessor :name, :options, :source
|
|
8
|
+
|
|
9
|
+
def initialize(source, name, options)
|
|
10
|
+
@name = name
|
|
11
|
+
@options = options
|
|
12
|
+
@source = source
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def empty?
|
|
16
|
+
records.empty?
|
|
17
|
+
end
|
|
18
|
+
alias :nil? :empty?
|
|
19
|
+
|
|
20
|
+
def size
|
|
21
|
+
records.count
|
|
22
|
+
end
|
|
23
|
+
alias :count :size
|
|
24
|
+
|
|
25
|
+
def include?(object)
|
|
26
|
+
records.include?(object)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def delete(object)
|
|
30
|
+
source.update_attribute(source_attribute, source_ids - Array(object).collect(&:id))
|
|
31
|
+
Array(object).collect{|o| self.send(:disassociate_target, o)} if target_association
|
|
32
|
+
object
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def <<(object)
|
|
36
|
+
source.update_attribute(source_attribute, source_ids.merge(Array(object).collect(&:id)))
|
|
37
|
+
Array(object).collect{|o| self.send(:associate_target, o)} if target_association
|
|
38
|
+
object
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def create(attributes = {})
|
|
42
|
+
object = target_class.create(attributes)
|
|
43
|
+
self << object
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def records
|
|
49
|
+
results = target_class.find(source_ids.to_a)
|
|
50
|
+
results.nil? ? [] : Array(results)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def target_class
|
|
54
|
+
name.to_s.singularize.capitalize.constantize
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def target_attribute
|
|
58
|
+
"#{target_association}_ids".to_sym if target_association
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def target_ids
|
|
62
|
+
target.send(target_attribute) || Set.new
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def source_class
|
|
66
|
+
source.class
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def source_attribute
|
|
70
|
+
"#{name}_ids".to_sym
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def source_ids
|
|
74
|
+
source.send(source_attribute) || Set.new
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Dynamoid #:nodoc:
|
|
3
|
+
|
|
4
|
+
# The BelongsTo association.
|
|
5
|
+
module Associations
|
|
6
|
+
class BelongsTo
|
|
7
|
+
include Dynamoid::Associations::Association
|
|
8
|
+
|
|
9
|
+
def ==(other)
|
|
10
|
+
target == other
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def target
|
|
16
|
+
records.first
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def target_association
|
|
20
|
+
has_many_key_name = source.class.to_s.pluralize.downcase.to_sym
|
|
21
|
+
has_one_key_name = source.class.to_s.downcase.to_sym
|
|
22
|
+
if !target_class.associations[has_many_key_name].nil?
|
|
23
|
+
return has_many_key_name if target_class.associations[has_many_key_name][:type] == :has_many
|
|
24
|
+
elsif !target_class.associations[has_one_key_name].nil?
|
|
25
|
+
return has_one_key_name if target_class.associations[has_one_key_name][:type] == :has_one
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def associate_target(object)
|
|
30
|
+
object.update_attribute(target_attribute, target_ids.merge(Array(source.id)))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def disassociate_target(object)
|
|
34
|
+
source.update_attribute(source_attribute, target_ids - Array(source.id))
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Dynamoid #:nodoc:
|
|
3
|
+
|
|
4
|
+
# The habtm association.
|
|
5
|
+
module Associations
|
|
6
|
+
class HasAndBelongsToMany
|
|
7
|
+
include Dynamoid::Associations::Association
|
|
8
|
+
|
|
9
|
+
def ==(other)
|
|
10
|
+
records == Array(other)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def target_association
|
|
16
|
+
key_name = source.class.to_s.pluralize.downcase.to_sym
|
|
17
|
+
guess = target_class.associations[key_name]
|
|
18
|
+
return nil if guess.nil? || guess[:type] != :has_and_belongs_to_many
|
|
19
|
+
key_name
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def associate_target(object)
|
|
23
|
+
ids = object.send(target_attribute) || Set.new
|
|
24
|
+
object.update_attribute(target_attribute, ids.merge(Array(source.id)))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def disassociate_target(object)
|
|
28
|
+
ids = object.send(target_attribute) || Set.new
|
|
29
|
+
source.update_attribute(source_attribute, ids - Array(source.id))
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Dynamoid #:nodoc:
|
|
3
|
+
|
|
4
|
+
# The HasMany association.
|
|
5
|
+
module Associations
|
|
6
|
+
class HasMany
|
|
7
|
+
include Dynamoid::Associations::Association
|
|
8
|
+
|
|
9
|
+
def ==(other)
|
|
10
|
+
records == Array(other)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def target_association
|
|
16
|
+
key_name = source.class.to_s.singularize.downcase.to_sym
|
|
17
|
+
guess = target_class.associations[key_name]
|
|
18
|
+
return nil if guess.nil? || guess[:type] != :belongs_to
|
|
19
|
+
key_name
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def associate_target(object)
|
|
23
|
+
object.update_attribute(target_attribute, Set[source.id])
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def disassociate_target(object)
|
|
27
|
+
object.update_attribute(target_attribute, nil)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Dynamoid #:nodoc:
|
|
3
|
+
|
|
4
|
+
# The HasOne association.
|
|
5
|
+
module Associations
|
|
6
|
+
class HasOne
|
|
7
|
+
include Dynamoid::Associations::Association
|
|
8
|
+
|
|
9
|
+
def ==(other)
|
|
10
|
+
target == other
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def target
|
|
16
|
+
records.first
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def target_association
|
|
20
|
+
key_name = source.class.to_s.pluralize.downcase.to_sym
|
|
21
|
+
guess = target_class.associations[key_name]
|
|
22
|
+
return nil if guess.nil? || guess[:type] != :belongs_to
|
|
23
|
+
key_name
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def associate_target(object)
|
|
27
|
+
object.update_attribute(target_attribute, source.id)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def disassociate_target(object)
|
|
31
|
+
source.update_attribute(source_attribute, nil)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Dynamoid #:nodoc:
|
|
3
|
+
|
|
4
|
+
module Attributes
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
attr_accessor :attributes
|
|
8
|
+
alias :raw_attributes :attributes
|
|
9
|
+
|
|
10
|
+
def write_attribute(name, value)
|
|
11
|
+
attributes[name.to_sym] = value
|
|
12
|
+
end
|
|
13
|
+
alias :[]= :write_attribute
|
|
14
|
+
|
|
15
|
+
def read_attribute(name)
|
|
16
|
+
attributes[name.to_sym]
|
|
17
|
+
end
|
|
18
|
+
alias :[] :read_attribute
|
|
19
|
+
|
|
20
|
+
def update_attributes(attributes)
|
|
21
|
+
self.attributes = attributes
|
|
22
|
+
save
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def update_attribute(attribute, value)
|
|
26
|
+
self.attributes[attribute] = value
|
|
27
|
+
save
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
module ClassMethods
|
|
31
|
+
def attributes
|
|
32
|
+
[self.fields + [:id]].flatten.uniq
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Dynamoid #:nodoc
|
|
3
|
+
module Components #:nodoc
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
# All modules that a +Document+ is composed of are defined in this
|
|
7
|
+
# module, to keep the document class from getting too cluttered.
|
|
8
|
+
included do
|
|
9
|
+
extend ActiveModel::Translation
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
include ActiveModel::Conversion
|
|
13
|
+
include ActiveModel::MassAssignmentSecurity
|
|
14
|
+
include ActiveModel::Naming
|
|
15
|
+
include ActiveModel::Observing
|
|
16
|
+
include ActiveModel::Serializers::JSON
|
|
17
|
+
include ActiveModel::Serializers::Xml
|
|
18
|
+
include Dynamoid::Attributes
|
|
19
|
+
include Dynamoid::Fields
|
|
20
|
+
include Dynamoid::Indexes
|
|
21
|
+
include Dynamoid::Persistence
|
|
22
|
+
include Dynamoid::Finders
|
|
23
|
+
include Dynamoid::Associations
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require "uri"
|
|
3
|
+
require "dynamoid/config/options"
|
|
4
|
+
|
|
5
|
+
module Dynamoid #:nodoc
|
|
6
|
+
|
|
7
|
+
module Config
|
|
8
|
+
extend self
|
|
9
|
+
extend Options
|
|
10
|
+
include ActiveModel::Observing
|
|
11
|
+
|
|
12
|
+
option :adapter, :default => 'local'
|
|
13
|
+
option :namespace, :default => 'dynamoid'
|
|
14
|
+
option :access_key
|
|
15
|
+
option :secret_key
|
|
16
|
+
option :warn_on_scan, :default => true
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
end
|