dynamoid 0.2.0 → 0.3.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/Dynamoid.gemspec +65 -3
- data/Gemfile +3 -0
- data/Gemfile.lock +6 -0
- data/README.markdown +117 -22
- data/Rakefile +22 -9
- data/VERSION +1 -1
- data/doc/.nojekyll +0 -0
- data/doc/Dynamoid.html +300 -0
- data/doc/Dynamoid/Adapter.html +1387 -0
- data/doc/Dynamoid/Adapter/AwsSdk.html +1561 -0
- data/doc/Dynamoid/Adapter/Local.html +1487 -0
- data/doc/Dynamoid/Associations.html +131 -0
- data/doc/Dynamoid/Associations/Association.html +1706 -0
- data/doc/Dynamoid/Associations/BelongsTo.html +339 -0
- data/doc/Dynamoid/Associations/ClassMethods.html +723 -0
- data/doc/Dynamoid/Associations/HasAndBelongsToMany.html +339 -0
- data/doc/Dynamoid/Associations/HasMany.html +339 -0
- data/doc/Dynamoid/Associations/HasOne.html +339 -0
- data/doc/Dynamoid/Components.html +202 -0
- data/doc/Dynamoid/Config.html +395 -0
- data/doc/Dynamoid/Config/Options.html +609 -0
- data/doc/Dynamoid/Criteria.html +131 -0
- data/doc/Dynamoid/Criteria/Chain.html +759 -0
- data/doc/Dynamoid/Criteria/ClassMethods.html +98 -0
- data/doc/Dynamoid/Document.html +512 -0
- data/doc/Dynamoid/Document/ClassMethods.html +581 -0
- data/doc/Dynamoid/Errors.html +118 -0
- data/doc/Dynamoid/Errors/DocumentNotValid.html +210 -0
- data/doc/Dynamoid/Errors/Error.html +130 -0
- data/doc/Dynamoid/Errors/InvalidField.html +133 -0
- data/doc/Dynamoid/Errors/MissingRangeKey.html +133 -0
- data/doc/Dynamoid/Fields.html +649 -0
- data/doc/Dynamoid/Fields/ClassMethods.html +264 -0
- data/doc/Dynamoid/Finders.html +128 -0
- data/doc/Dynamoid/Finders/ClassMethods.html +502 -0
- data/doc/Dynamoid/Indexes.html +308 -0
- data/doc/Dynamoid/Indexes/ClassMethods.html +351 -0
- data/doc/Dynamoid/Indexes/Index.html +1089 -0
- data/doc/Dynamoid/Persistence.html +653 -0
- data/doc/Dynamoid/Persistence/ClassMethods.html +568 -0
- data/doc/Dynamoid/Validations.html +399 -0
- data/doc/_index.html +439 -0
- data/doc/class_list.html +47 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +55 -0
- data/doc/css/style.css +322 -0
- data/doc/file.LICENSE.html +66 -0
- data/doc/file.README.html +279 -0
- data/doc/file_list.html +52 -0
- data/doc/frames.html +13 -0
- data/doc/index.html +279 -0
- data/doc/js/app.js +205 -0
- data/doc/js/full_list.js +173 -0
- data/doc/js/jquery.js +16 -0
- data/doc/method_list.html +1054 -0
- data/doc/top-level-namespace.html +105 -0
- data/lib/dynamoid.rb +2 -1
- data/lib/dynamoid/adapter.rb +77 -6
- data/lib/dynamoid/adapter/aws_sdk.rb +96 -16
- data/lib/dynamoid/adapter/local.rb +84 -15
- data/lib/dynamoid/associations.rb +53 -4
- data/lib/dynamoid/associations/association.rb +154 -26
- data/lib/dynamoid/associations/belongs_to.rb +32 -6
- data/lib/dynamoid/associations/has_and_belongs_to_many.rb +29 -3
- data/lib/dynamoid/associations/has_many.rb +30 -4
- data/lib/dynamoid/associations/has_one.rb +26 -3
- data/lib/dynamoid/components.rb +7 -5
- data/lib/dynamoid/config.rb +15 -2
- data/lib/dynamoid/config/options.rb +8 -0
- data/lib/dynamoid/criteria.rb +7 -2
- data/lib/dynamoid/criteria/chain.rb +55 -8
- data/lib/dynamoid/document.rb +68 -7
- data/lib/dynamoid/errors.rb +17 -2
- data/lib/dynamoid/fields.rb +44 -1
- data/lib/dynamoid/finders.rb +32 -6
- data/lib/dynamoid/indexes.rb +22 -2
- data/lib/dynamoid/indexes/index.rb +48 -7
- data/lib/dynamoid/persistence.rb +111 -51
- data/lib/dynamoid/validations.rb +36 -0
- data/spec/app/models/address.rb +2 -1
- data/spec/app/models/camel_case.rb +11 -0
- data/spec/app/models/magazine.rb +4 -1
- data/spec/app/models/sponsor.rb +3 -1
- data/spec/app/models/subscription.rb +5 -1
- data/spec/app/models/user.rb +10 -1
- data/spec/dynamoid/associations/association_spec.rb +67 -1
- data/spec/dynamoid/associations/belongs_to_spec.rb +16 -1
- data/spec/dynamoid/associations/has_and_belongs_to_many_spec.rb +7 -0
- data/spec/dynamoid/associations/has_many_spec.rb +14 -0
- data/spec/dynamoid/associations/has_one_spec.rb +10 -1
- data/spec/dynamoid/criteria_spec.rb +5 -1
- data/spec/dynamoid/document_spec.rb +23 -3
- data/spec/dynamoid/fields_spec.rb +10 -1
- data/spec/dynamoid/indexes/index_spec.rb +19 -0
- data/spec/dynamoid/persistence_spec.rb +24 -0
- data/spec/dynamoid/validations_spec.rb +36 -0
- metadata +105 -4
@@ -1,15 +1,24 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module Dynamoid #:nodoc:
|
3
3
|
|
4
|
-
# The
|
4
|
+
# The belongs_to association. For belongs_to, we reference only a single target instead of multiple records; that target is the
|
5
|
+
# object to which the association object is associated.
|
5
6
|
module Associations
|
6
7
|
class BelongsTo
|
7
8
|
include Dynamoid::Associations::Association
|
8
9
|
|
10
|
+
# Is this object equal to the association's target?
|
11
|
+
#
|
12
|
+
# @return [Boolean] true/false
|
13
|
+
#
|
14
|
+
# @since 0.2.0
|
9
15
|
def ==(other)
|
10
16
|
target == other
|
11
17
|
end
|
12
18
|
|
19
|
+
# Delegate methods we don't find directly to the target.
|
20
|
+
#
|
21
|
+
# @since 0.2.0
|
13
22
|
def method_missing(method, *args)
|
14
23
|
if target.respond_to?(method)
|
15
24
|
target.send(method, *args)
|
@@ -20,28 +29,45 @@ module Dynamoid #:nodoc:
|
|
20
29
|
|
21
30
|
private
|
22
31
|
|
32
|
+
# Find the target of the belongs_to association.
|
33
|
+
#
|
34
|
+
# @return [Dynamoid::Document] the found target (or nil if nothing)
|
35
|
+
#
|
36
|
+
# @since 0.2.0
|
23
37
|
def target
|
24
38
|
records.first
|
25
39
|
end
|
26
40
|
|
41
|
+
# Find the target association, either has_many or has_one. Uses either options[:inverse_of] or the source class name and default parsing to
|
42
|
+
# return the most likely name for the target association.
|
43
|
+
#
|
44
|
+
# @since 0.2.0
|
27
45
|
def target_association
|
28
|
-
has_many_key_name = source.class.to_s.pluralize.
|
29
|
-
has_one_key_name = source.class.to_s.
|
46
|
+
has_many_key_name = options[:inverse_of] || source.class.to_s.underscore.pluralize.to_sym
|
47
|
+
has_one_key_name = options[:inverse_of] || source.class.to_s.underscore.to_sym
|
30
48
|
if !target_class.associations[has_many_key_name].nil?
|
31
49
|
return has_many_key_name if target_class.associations[has_many_key_name][:type] == :has_many
|
32
|
-
|
50
|
+
end
|
51
|
+
|
52
|
+
if !target_class.associations[has_one_key_name].nil?
|
33
53
|
return has_one_key_name if target_class.associations[has_one_key_name][:type] == :has_one
|
34
54
|
end
|
35
55
|
end
|
36
56
|
|
57
|
+
# Associate a source object to this association.
|
58
|
+
#
|
59
|
+
# @since 0.2.0
|
37
60
|
def associate_target(object)
|
38
61
|
object.update_attribute(target_attribute, target_ids.merge(Array(source.id)))
|
39
62
|
end
|
40
|
-
|
63
|
+
|
64
|
+
# Disassociate a source object from this association.
|
65
|
+
#
|
66
|
+
# @since 0.2.0
|
41
67
|
def disassociate_target(object)
|
42
68
|
source.update_attribute(source_attribute, target_ids - Array(source.id))
|
43
69
|
end
|
44
70
|
end
|
45
71
|
end
|
46
72
|
|
47
|
-
end
|
73
|
+
end
|
@@ -1,29 +1,55 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module Dynamoid #:nodoc:
|
3
3
|
|
4
|
-
# The
|
4
|
+
# The has and belongs to many association.
|
5
5
|
module Associations
|
6
6
|
class HasAndBelongsToMany
|
7
7
|
include Dynamoid::Associations::Association
|
8
8
|
|
9
|
+
# Is this array equal to the association's records?
|
10
|
+
#
|
11
|
+
# @return [Boolean] true/false
|
12
|
+
#
|
13
|
+
# @since 0.2.0
|
9
14
|
def ==(other)
|
10
15
|
records == Array(other)
|
11
16
|
end
|
12
17
|
|
18
|
+
# Delegate methods we don't find directly to the records array.
|
19
|
+
#
|
20
|
+
# @since 0.2.0
|
21
|
+
def method_missing(method, *args)
|
22
|
+
if records.respond_to?(method)
|
23
|
+
records.send(method, *args)
|
24
|
+
else
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
13
29
|
private
|
14
30
|
|
31
|
+
# Find the target association, always another :has_and_belongs_to_many association. Uses either options[:inverse_of] or the source class name
|
32
|
+
# and default parsing to return the most likely name for the target association.
|
33
|
+
#
|
34
|
+
# @since 0.2.0
|
15
35
|
def target_association
|
16
|
-
key_name = source.class.to_s.pluralize.
|
36
|
+
key_name = options[:inverse_of] || source.class.to_s.pluralize.underscore.to_sym
|
17
37
|
guess = target_class.associations[key_name]
|
18
38
|
return nil if guess.nil? || guess[:type] != :has_and_belongs_to_many
|
19
39
|
key_name
|
20
40
|
end
|
21
41
|
|
42
|
+
# Associate a source object to this association.
|
43
|
+
#
|
44
|
+
# @since 0.2.0
|
22
45
|
def associate_target(object)
|
23
46
|
ids = object.send(target_attribute) || Set.new
|
24
47
|
object.update_attribute(target_attribute, ids.merge(Array(source.id)))
|
25
48
|
end
|
26
49
|
|
50
|
+
# Disassociate a source object from this association.
|
51
|
+
#
|
52
|
+
# @since 0.2.0
|
27
53
|
def disassociate_target(object)
|
28
54
|
ids = object.send(target_attribute) || Set.new
|
29
55
|
object.update_attribute(target_attribute, ids - Array(source.id))
|
@@ -31,4 +57,4 @@ module Dynamoid #:nodoc:
|
|
31
57
|
end
|
32
58
|
end
|
33
59
|
|
34
|
-
end
|
60
|
+
end
|
@@ -1,28 +1,54 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module Dynamoid #:nodoc:
|
3
3
|
|
4
|
-
# The
|
4
|
+
# The has_many association.
|
5
5
|
module Associations
|
6
6
|
class HasMany
|
7
7
|
include Dynamoid::Associations::Association
|
8
8
|
|
9
|
+
# Is this array equal to the association's records?
|
10
|
+
#
|
11
|
+
# @return [Boolean] true/false
|
12
|
+
#
|
13
|
+
# @since 0.2.0
|
9
14
|
def ==(other)
|
10
15
|
records == Array(other)
|
11
16
|
end
|
12
17
|
|
18
|
+
# Delegate methods we don't find directly to the records array.
|
19
|
+
#
|
20
|
+
# @since 0.2.0
|
21
|
+
def method_missing(method, *args)
|
22
|
+
if records.respond_to?(method)
|
23
|
+
records.send(method, *args)
|
24
|
+
else
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
13
29
|
private
|
14
30
|
|
31
|
+
# Find the target association, always a :belongs_to association. Uses either options[:inverse_of] or the source class name
|
32
|
+
# and default parsing to return the most likely name for the target association.
|
33
|
+
#
|
34
|
+
# @since 0.2.0
|
15
35
|
def target_association
|
16
|
-
key_name = source.class.to_s.singularize.
|
36
|
+
key_name = options[:inverse_of] || source.class.to_s.singularize.underscore.to_sym
|
17
37
|
guess = target_class.associations[key_name]
|
18
38
|
return nil if guess.nil? || guess[:type] != :belongs_to
|
19
39
|
key_name
|
20
40
|
end
|
21
|
-
|
41
|
+
|
42
|
+
# Associate a source object to this association.
|
43
|
+
#
|
44
|
+
# @since 0.2.0
|
22
45
|
def associate_target(object)
|
23
46
|
object.update_attribute(target_attribute, Set[source.id])
|
24
47
|
end
|
25
48
|
|
49
|
+
# Disassociate a source object from this association.
|
50
|
+
#
|
51
|
+
# @since 0.2.0
|
26
52
|
def disassociate_target(object)
|
27
53
|
object.update_attribute(target_attribute, nil)
|
28
54
|
end
|
@@ -30,4 +56,4 @@ module Dynamoid #:nodoc:
|
|
30
56
|
end
|
31
57
|
end
|
32
58
|
|
33
|
-
end
|
59
|
+
end
|
@@ -6,10 +6,18 @@ module Dynamoid #:nodoc:
|
|
6
6
|
class HasOne
|
7
7
|
include Dynamoid::Associations::Association
|
8
8
|
|
9
|
+
# Is this object equal to the association's target?
|
10
|
+
#
|
11
|
+
# @return [Boolean] true/false
|
12
|
+
#
|
13
|
+
# @since 0.2.0
|
9
14
|
def ==(other)
|
10
15
|
target == other
|
11
16
|
end
|
12
17
|
|
18
|
+
# Delegate methods we don't find directly to the target.
|
19
|
+
#
|
20
|
+
# @since 0.2.0
|
13
21
|
def method_missing(method, *args)
|
14
22
|
if target.respond_to?(method)
|
15
23
|
target.send(method, *args)
|
@@ -20,21 +28,36 @@ module Dynamoid #:nodoc:
|
|
20
28
|
|
21
29
|
private
|
22
30
|
|
31
|
+
# Find the target of the has_one association.
|
32
|
+
#
|
33
|
+
# @return [Dynamoid::Document] the found target (or nil if nothing)
|
34
|
+
#
|
35
|
+
# @since 0.2.0
|
23
36
|
def target
|
24
37
|
records.first
|
25
38
|
end
|
26
|
-
|
39
|
+
|
40
|
+
# Find the target association, always a :belongs_to association. Uses either options[:inverse_of] or the source class name
|
41
|
+
# and default parsing to return the most likely name for the target association.
|
42
|
+
#
|
43
|
+
# @since 0.2.0
|
27
44
|
def target_association
|
28
|
-
key_name = source.class.to_s.singularize.
|
45
|
+
key_name = options[:inverse_of] || source.class.to_s.singularize.underscore.to_sym
|
29
46
|
guess = target_class.associations[key_name]
|
30
47
|
return nil if guess.nil? || guess[:type] != :belongs_to
|
31
48
|
key_name
|
32
49
|
end
|
33
50
|
|
51
|
+
# Associate a source object to this association.
|
52
|
+
#
|
53
|
+
# @since 0.2.0
|
34
54
|
def associate_target(object)
|
35
|
-
object.update_attribute(target_attribute, source.id)
|
55
|
+
object.update_attribute(target_attribute, Set[source.id])
|
36
56
|
end
|
37
57
|
|
58
|
+
# Disassociate a source object from this association.
|
59
|
+
#
|
60
|
+
# @since 0.2.0
|
38
61
|
def disassociate_target(object)
|
39
62
|
source.update_attribute(source_attribute, nil)
|
40
63
|
end
|
data/lib/dynamoid/components.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
module Dynamoid
|
3
|
-
|
2
|
+
module Dynamoid
|
3
|
+
|
4
|
+
# All modules that a Document is composed of are defined in this
|
5
|
+
# module, to keep the document class from getting too cluttered.
|
6
|
+
module Components
|
4
7
|
extend ActiveSupport::Concern
|
5
8
|
|
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
9
|
included do
|
9
10
|
extend ActiveModel::Translation
|
10
11
|
extend ActiveModel::Callbacks
|
@@ -16,10 +17,10 @@ module Dynamoid #:nodoc
|
|
16
17
|
end
|
17
18
|
|
18
19
|
include ActiveModel::Conversion
|
20
|
+
include ActiveModel::Dirty
|
19
21
|
include ActiveModel::MassAssignmentSecurity
|
20
22
|
include ActiveModel::Naming
|
21
23
|
include ActiveModel::Observing
|
22
|
-
include ActiveModel::Validations
|
23
24
|
include ActiveModel::Serializers::JSON
|
24
25
|
include ActiveModel::Serializers::Xml
|
25
26
|
include Dynamoid::Fields
|
@@ -28,5 +29,6 @@ module Dynamoid #:nodoc
|
|
28
29
|
include Dynamoid::Finders
|
29
30
|
include Dynamoid::Associations
|
30
31
|
include Dynamoid::Criteria
|
32
|
+
include Dynamoid::Validations
|
31
33
|
end
|
32
34
|
end
|
data/lib/dynamoid/config.rb
CHANGED
@@ -2,31 +2,44 @@
|
|
2
2
|
require "uri"
|
3
3
|
require "dynamoid/config/options"
|
4
4
|
|
5
|
-
module Dynamoid
|
5
|
+
module Dynamoid
|
6
6
|
|
7
|
+
# Contains all the basic configuration information required for Dynamoid: both sensible defaults and required fields.
|
7
8
|
module Config
|
8
9
|
extend self
|
9
10
|
extend Options
|
10
11
|
include ActiveModel::Observing
|
11
12
|
|
13
|
+
# All the default options.
|
12
14
|
option :adapter, :default => 'local'
|
13
15
|
option :namespace, :default => defined?(Rails) ? "dynamoid_#{Rails.application.class.parent_name}_#{Rails.env}" : "dynamoid"
|
14
16
|
option :logger, :default => defined?(Rails)
|
15
17
|
option :access_key
|
16
18
|
option :secret_key
|
19
|
+
option :read_capacity, :default => 100
|
20
|
+
option :write_capacity, :default => 20
|
17
21
|
option :warn_on_scan, :default => true
|
18
22
|
option :partitioning, :default => false
|
19
23
|
option :partition_size, :default => 200
|
20
24
|
option :included_models, :default => []
|
21
25
|
|
26
|
+
# The default logger for Dynamoid: either the Rails logger or just stdout.
|
27
|
+
#
|
28
|
+
# @since 0.2.0
|
22
29
|
def default_logger
|
23
30
|
defined?(Rails) && Rails.respond_to?(:logger) ? Rails.logger : ::Logger.new($stdout)
|
24
31
|
end
|
25
32
|
|
33
|
+
# Returns the assigned logger instance.
|
34
|
+
#
|
35
|
+
# @since 0.2.0
|
26
36
|
def logger
|
27
37
|
@logger ||= default_logger
|
28
38
|
end
|
29
39
|
|
40
|
+
# If you want to, set the logger manually to any output you'd like. Or pass false or nil to disable logging entirely.
|
41
|
+
#
|
42
|
+
# @since 0.2.0
|
30
43
|
def logger=(logger)
|
31
44
|
case logger
|
32
45
|
when false, nil then @logger = nil
|
@@ -37,4 +50,4 @@ module Dynamoid #:nodoc
|
|
37
50
|
end
|
38
51
|
|
39
52
|
end
|
40
|
-
end
|
53
|
+
end
|
@@ -11,6 +11,8 @@ module Dynamoid #:nodoc
|
|
11
11
|
# options.defaults
|
12
12
|
#
|
13
13
|
# @return [ Hash ] The default options.
|
14
|
+
#
|
15
|
+
# @since 0.2.0
|
14
16
|
def defaults
|
15
17
|
@defaults ||= {}
|
16
18
|
end
|
@@ -24,6 +26,8 @@ module Dynamoid #:nodoc
|
|
24
26
|
# @param [ Hash ] options Extras for the option.
|
25
27
|
#
|
26
28
|
# @option options [ Object ] :default The default value.
|
29
|
+
#
|
30
|
+
# @since 0.2.0
|
27
31
|
def option(name, options = {})
|
28
32
|
defaults[name] = settings[name] = options[:default]
|
29
33
|
|
@@ -52,6 +56,8 @@ module Dynamoid #:nodoc
|
|
52
56
|
# config.reset
|
53
57
|
#
|
54
58
|
# @return [ Hash ] The defaults.
|
59
|
+
#
|
60
|
+
# @since 0.2.0
|
55
61
|
def reset
|
56
62
|
settings.replace(defaults)
|
57
63
|
end
|
@@ -62,6 +68,8 @@ module Dynamoid #:nodoc
|
|
62
68
|
# options.settings
|
63
69
|
#
|
64
70
|
# @return [ Hash ] The setting options.
|
71
|
+
#
|
72
|
+
# @since 0.2.0
|
65
73
|
def settings
|
66
74
|
@settings ||= {}
|
67
75
|
end
|
data/lib/dynamoid/criteria.rb
CHANGED
@@ -1,14 +1,19 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'dynamoid/criteria/chain'
|
3
3
|
|
4
|
-
module Dynamoid
|
4
|
+
module Dynamoid
|
5
5
|
|
6
|
-
#
|
6
|
+
# Allows classes to be queried by where, all, first, and each and return criteria chains.
|
7
7
|
module Criteria
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
10
|
module ClassMethods
|
11
|
+
|
11
12
|
[:where, :all, :first, :each].each do |meth|
|
13
|
+
# Return a criteria chain in response to a method that will begin or end a chain. For more information,
|
14
|
+
# see Dynamoid::Criteria::Chain.
|
15
|
+
#
|
16
|
+
# @since 0.2.0
|
12
17
|
define_method(meth) do |*args|
|
13
18
|
chain = Dynamoid::Criteria::Chain.new(self)
|
14
19
|
if args
|
@@ -2,41 +2,75 @@
|
|
2
2
|
module Dynamoid #:nodoc:
|
3
3
|
module Criteria
|
4
4
|
|
5
|
-
# The
|
6
|
-
#
|
5
|
+
# The criteria chain is equivalent to an ActiveRecord relation (and realistically I should change the name from
|
6
|
+
# chain to relation). It is a chainable object that builds up a query and eventually executes it either on an index
|
7
|
+
# or by a full table scan.
|
7
8
|
class Chain
|
8
9
|
attr_accessor :query, :source, :index, :values
|
9
10
|
include Enumerable
|
10
11
|
|
12
|
+
# Create a new criteria chain.
|
13
|
+
#
|
14
|
+
# @param [Class] source the class upon which the ultimate query will be performed.
|
11
15
|
def initialize(source)
|
12
16
|
@query = {}
|
13
17
|
@source = source
|
14
18
|
end
|
15
19
|
|
20
|
+
# The workhorse method of the criteria chain. Each key in the passed in hash will become another criteria that the
|
21
|
+
# ultimate query must match. A key can either be a symbol or a string, and should be an attribute name or
|
22
|
+
# an attribute name with a range operator.
|
23
|
+
#
|
24
|
+
# @example A simple criteria
|
25
|
+
# where(:name => 'Josh')
|
26
|
+
#
|
27
|
+
# @example A more complicated criteria
|
28
|
+
# where(:name => 'Josh', 'created_at.gt' => DateTime.now - 1.day)
|
29
|
+
#
|
30
|
+
# @since 0.2.0
|
16
31
|
def where(args)
|
17
32
|
args.each {|k, v| query[k] = v}
|
18
33
|
self
|
19
34
|
end
|
20
35
|
|
36
|
+
# Returns all the records matching the criteria.
|
37
|
+
#
|
38
|
+
# @since 0.2.0
|
21
39
|
def all
|
22
40
|
records
|
23
41
|
end
|
24
|
-
|
42
|
+
|
43
|
+
# Returns the first record matching the criteria.
|
44
|
+
#
|
45
|
+
# @since 0.2.0
|
25
46
|
def first
|
26
47
|
records.first
|
27
48
|
end
|
28
|
-
|
49
|
+
|
50
|
+
# Allows you to use the results of a search as an enumerable over the results found.
|
51
|
+
#
|
52
|
+
# @since 0.2.0
|
29
53
|
def each(&block)
|
30
54
|
records.each(&block)
|
31
55
|
end
|
32
56
|
|
33
57
|
private
|
34
58
|
|
59
|
+
# The actual records referenced by the association.
|
60
|
+
#
|
61
|
+
# @return [Array] an array of the found records.
|
62
|
+
#
|
63
|
+
# @since 0.2.0
|
35
64
|
def records
|
36
65
|
return records_with_index if index
|
37
66
|
records_without_index
|
38
67
|
end
|
39
|
-
|
68
|
+
|
69
|
+
# If the query matches an index on the associated class, then this method will retrieve results from the index table.
|
70
|
+
#
|
71
|
+
# @return [Array] an array of the found records.
|
72
|
+
#
|
73
|
+
# @since 0.2.0
|
40
74
|
def records_with_index
|
41
75
|
ids = if index.range_key?
|
42
76
|
Dynamoid::Adapter.query(index.table_name, index_query).collect{|r| r[:ids]}.inject(Set.new) {|set, result| set + result}
|
@@ -55,14 +89,24 @@ module Dynamoid #:nodoc:
|
|
55
89
|
end
|
56
90
|
end
|
57
91
|
|
92
|
+
# If the query does not match an index, we'll manually scan the associated table to manually find results.
|
93
|
+
#
|
94
|
+
# @return [Array] an array of the found records.
|
95
|
+
#
|
96
|
+
# @since 0.2.0
|
58
97
|
def records_without_index
|
59
98
|
if Dynamoid::Config.warn_on_scan
|
60
99
|
Dynamoid.logger.warn 'Queries without an index are forced to use scan and are generally much slower than indexed queries!'
|
61
100
|
Dynamoid.logger.warn "You can index this query by adding this to #{source.to_s.downcase}.rb: index [#{source.attributes.sort.collect{|attr| ":#{attr}"}.join(', ')}]"
|
62
101
|
end
|
63
|
-
Dynamoid::Adapter.scan(source.table_name, query).collect {|hash| source.new(hash)}
|
102
|
+
Dynamoid::Adapter.scan(source.table_name, query).collect {|hash| source.new(hash).tap { |r| r.new_record = false } }
|
64
103
|
end
|
65
104
|
|
105
|
+
# Format the provided query so that it can be used to query results from DynamoDB.
|
106
|
+
#
|
107
|
+
# @return [Hash] a hash with keys of :hash_value and :range_value
|
108
|
+
#
|
109
|
+
# @since 0.2.0
|
66
110
|
def index_query
|
67
111
|
values = index.values(query)
|
68
112
|
{}.tap do |hash|
|
@@ -91,7 +135,10 @@ module Dynamoid #:nodoc:
|
|
91
135
|
end
|
92
136
|
end
|
93
137
|
end
|
94
|
-
|
138
|
+
|
139
|
+
# Return an index that fulfills all the attributes the criteria is querying, or nil if none is found.
|
140
|
+
#
|
141
|
+
# @since 0.2.0
|
95
142
|
def index
|
96
143
|
index = source.find_index(query.keys.collect{|k| k.to_s.split('.').first})
|
97
144
|
return nil if index.blank?
|
@@ -101,4 +148,4 @@ module Dynamoid #:nodoc:
|
|
101
148
|
|
102
149
|
end
|
103
150
|
|
104
|
-
end
|
151
|
+
end
|