dynamoid 3.2.0 → 3.6.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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +111 -1
  3. data/README.md +580 -241
  4. data/lib/dynamoid.rb +2 -0
  5. data/lib/dynamoid/adapter.rb +15 -15
  6. data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +82 -102
  7. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/batch_get_item.rb +108 -0
  8. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/create_table.rb +29 -16
  9. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb +3 -2
  10. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/backoff.rb +2 -2
  11. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/limit.rb +2 -3
  12. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/start_key.rb +2 -2
  13. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb +15 -6
  14. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +15 -5
  15. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/table.rb +1 -0
  16. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/until_past_table_status.rb +5 -3
  17. data/lib/dynamoid/application_time_zone.rb +1 -0
  18. data/lib/dynamoid/associations.rb +182 -19
  19. data/lib/dynamoid/associations/association.rb +4 -2
  20. data/lib/dynamoid/associations/belongs_to.rb +2 -1
  21. data/lib/dynamoid/associations/has_and_belongs_to_many.rb +2 -1
  22. data/lib/dynamoid/associations/has_many.rb +2 -1
  23. data/lib/dynamoid/associations/has_one.rb +2 -1
  24. data/lib/dynamoid/associations/many_association.rb +65 -22
  25. data/lib/dynamoid/associations/single_association.rb +28 -1
  26. data/lib/dynamoid/components.rb +8 -3
  27. data/lib/dynamoid/config.rb +16 -3
  28. data/lib/dynamoid/config/backoff_strategies/constant_backoff.rb +1 -0
  29. data/lib/dynamoid/config/backoff_strategies/exponential_backoff.rb +1 -0
  30. data/lib/dynamoid/config/options.rb +1 -0
  31. data/lib/dynamoid/criteria.rb +2 -1
  32. data/lib/dynamoid/criteria/chain.rb +418 -46
  33. data/lib/dynamoid/criteria/ignored_conditions_detector.rb +3 -3
  34. data/lib/dynamoid/criteria/key_fields_detector.rb +109 -32
  35. data/lib/dynamoid/criteria/nonexistent_fields_detector.rb +3 -2
  36. data/lib/dynamoid/criteria/overwritten_conditions_detector.rb +1 -1
  37. data/lib/dynamoid/dirty.rb +239 -32
  38. data/lib/dynamoid/document.rb +130 -251
  39. data/lib/dynamoid/dumping.rb +9 -0
  40. data/lib/dynamoid/dynamodb_time_zone.rb +1 -0
  41. data/lib/dynamoid/fields.rb +246 -20
  42. data/lib/dynamoid/finders.rb +69 -32
  43. data/lib/dynamoid/identity_map.rb +6 -0
  44. data/lib/dynamoid/indexes.rb +76 -17
  45. data/lib/dynamoid/loadable.rb +31 -0
  46. data/lib/dynamoid/log/formatter.rb +26 -0
  47. data/lib/dynamoid/middleware/identity_map.rb +1 -0
  48. data/lib/dynamoid/persistence.rb +592 -122
  49. data/lib/dynamoid/persistence/import.rb +73 -0
  50. data/lib/dynamoid/persistence/save.rb +64 -0
  51. data/lib/dynamoid/persistence/update_fields.rb +63 -0
  52. data/lib/dynamoid/persistence/upsert.rb +60 -0
  53. data/lib/dynamoid/primary_key_type_mapping.rb +1 -0
  54. data/lib/dynamoid/railtie.rb +1 -0
  55. data/lib/dynamoid/tasks.rb +3 -1
  56. data/lib/dynamoid/tasks/database.rb +1 -0
  57. data/lib/dynamoid/type_casting.rb +12 -2
  58. data/lib/dynamoid/undumping.rb +8 -0
  59. data/lib/dynamoid/validations.rb +2 -0
  60. data/lib/dynamoid/version.rb +1 -1
  61. metadata +49 -71
  62. data/.coveralls.yml +0 -1
  63. data/.document +0 -5
  64. data/.gitignore +0 -74
  65. data/.rspec +0 -2
  66. data/.rubocop.yml +0 -71
  67. data/.rubocop_todo.yml +0 -55
  68. data/.travis.yml +0 -41
  69. data/Appraisals +0 -28
  70. data/Gemfile +0 -8
  71. data/Rakefile +0 -46
  72. data/Vagrantfile +0 -29
  73. data/docker-compose.yml +0 -7
  74. data/dynamoid.gemspec +0 -57
  75. data/gemfiles/rails_4_2.gemfile +0 -11
  76. data/gemfiles/rails_5_0.gemfile +0 -10
  77. data/gemfiles/rails_5_1.gemfile +0 -10
  78. data/gemfiles/rails_5_2.gemfile +0 -10
@@ -3,6 +3,7 @@
3
3
  require_relative 'until_past_table_status'
4
4
 
5
5
  module Dynamoid
6
+ # @private
6
7
  module AdapterPlugin
7
8
  class AwsSdkV3
8
9
  class CreateTable
@@ -16,6 +17,7 @@ module Dynamoid
16
17
  end
17
18
 
18
19
  def call
20
+ billing_mode = options[:billing_mode]
19
21
  read_capacity = options[:read_capacity] || Dynamoid::Config.read_capacity
20
22
  write_capacity = options[:write_capacity] || Dynamoid::Config.write_capacity
21
23
 
@@ -41,14 +43,20 @@ module Dynamoid
41
43
 
42
44
  client_opts = {
43
45
  table_name: table_name,
44
- provisioned_throughput: {
45
- read_capacity_units: read_capacity,
46
- write_capacity_units: write_capacity
47
- },
48
46
  key_schema: key_schema,
49
47
  attribute_definitions: attribute_definitions
50
48
  }
51
49
 
50
+ if billing_mode == :on_demand
51
+ client_opts[:billing_mode] = 'PAY_PER_REQUEST'
52
+ else
53
+ client_opts[:billing_mode] = 'PROVISIONED'
54
+ client_opts[:provisioned_throughput] = {
55
+ read_capacity_units: read_capacity,
56
+ write_capacity_units: write_capacity
57
+ }
58
+ end
59
+
52
60
  if ls_indexes.present?
53
61
  client_opts[:local_secondary_indexes] = ls_indexes.map do |index|
54
62
  index_to_aws_hash(index)
@@ -62,11 +70,16 @@ module Dynamoid
62
70
  end
63
71
  resp = client.create_table(client_opts)
64
72
  options[:sync] = true if !options.key?(:sync) && ls_indexes.present? || gs_indexes.present?
65
- UntilPastTableStatus.new(table_name, :creating).call if options[:sync] &&
66
- (status = PARSE_TABLE_STATUS.call(resp, :table_description)) &&
67
- status == TABLE_STATUSES[:creating]
73
+
74
+ if options[:sync]
75
+ status = PARSE_TABLE_STATUS.call(resp, :table_description)
76
+ if status == TABLE_STATUSES[:creating]
77
+ UntilPastTableStatus.new(client, table_name, :creating).call
78
+ end
79
+ end
80
+
68
81
  # Response to original create_table, which, if options[:sync]
69
- # may have an outdated table_description.table_status of "CREATING"
82
+ # may have an outdated table_description.table_status of "CREATING"
70
83
  resp
71
84
  end
72
85
 
@@ -75,9 +88,9 @@ module Dynamoid
75
88
  # Builds aws attributes definitions based off of primary hash/range and
76
89
  # secondary indexes
77
90
  #
78
- # @param key_data
79
- # @option key_data [Hash] hash_key_schema - eg: {:id => :string}
80
- # @option key_data [Hash] range_key_schema - eg: {:created_at => :number}
91
+ # @param key_schema
92
+ # @option key_schema [Hash] hash_key_schema - eg: {:id => :string}
93
+ # @option key_schema [Hash] range_key_schema - eg: {:created_at => :number}
81
94
  # @param [Hash] secondary_indexes
82
95
  # @option secondary_indexes [Array<Dynamoid::Indexes::Index>] :local_secondary_indexes
83
96
  # @option secondary_indexes [Array<Dynamoid::Indexes::Index>] :global_secondary_indexes
@@ -118,8 +131,8 @@ module Dynamoid
118
131
  end
119
132
 
120
133
  # Builds an attribute definitions based on hash key and range key
121
- # @params [Hash] hash_key_schema - eg: {:id => :string}
122
- # @params [Hash] range_key_schema - eg: {:created_at => :datetime}
134
+ # @param [Hash] hash_key_schema - eg: {:id => :string}
135
+ # @param [Hash] range_key_schema - eg: {:created_at => :datetime}
123
136
  # @return [Array]
124
137
  def build_attribute_definitions(hash_key_schema, range_key_schema = nil)
125
138
  attrs = []
@@ -140,8 +153,8 @@ module Dynamoid
140
153
  end
141
154
 
142
155
  # Builds an aws attribute definition based on name and dynamoid type
143
- # @params [Symbol] name - eg: :id
144
- # @params [Symbol] dynamoid_type - eg: :string
156
+ # @param [Symbol] name - eg: :id
157
+ # @param [Symbol] dynamoid_type - eg: :string
145
158
  # @return [Hash]
146
159
  def attribute_definition_element(name, dynamoid_type)
147
160
  aws_type = api_type(dynamoid_type)
@@ -201,7 +214,7 @@ module Dynamoid
201
214
  end
202
215
 
203
216
  # Only global secondary indexes have a separate throughput.
204
- if index.type == :global_secondary
217
+ if index.type == :global_secondary && options[:billing_mode] != :on_demand
205
218
  hash[:provisioned_throughput] = {
206
219
  read_capacity_units: index.read_capacity,
207
220
  write_capacity_units: index.write_capacity
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dynamoid
4
+ # @private
4
5
  module AdapterPlugin
5
6
  class AwsSdkV3
6
7
  # Mimics behavior of the yielded object on DynamoDB's update_item API (high level).
@@ -20,8 +21,8 @@ module Dynamoid
20
21
  # Adds the given values to the values already stored in the corresponding columns.
21
22
  # The column must contain a Set or a number.
22
23
  #
23
- # @param [Hash] vals keys of the hash are the columns to update, vals are the values to
24
- # add. values must be a Set, Array, or Numeric
24
+ # @param [Hash] values keys of the hash are the columns to update, values
25
+ # are the values to add. values must be a Set, Array, or Numeric
25
26
  #
26
27
  def add(values)
27
28
  @additions.merge!(sanitize_attributes(values))
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dynamoid
4
+ # @private
4
5
  module AdapterPlugin
5
6
  class AwsSdkV3
6
7
  module Middleware
@@ -14,11 +15,10 @@ module Dynamoid
14
15
  response = @next_chain.call(request)
15
16
  @backoff.call if @backoff
16
17
 
17
- return response
18
+ response
18
19
  end
19
20
  end
20
21
  end
21
22
  end
22
23
  end
23
24
  end
24
-
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dynamoid
4
+ # @private
4
5
  module AdapterPlugin
5
6
  class AwsSdkV3
6
7
  module Middleware
@@ -46,12 +47,10 @@ module Dynamoid
46
47
  @scan_count += response.scanned_count
47
48
  throw :stop_pagination if @scan_limit && @scan_count >= @scan_limit
48
49
 
49
- return response
50
+ response
50
51
  end
51
52
  end
52
-
53
53
  end
54
54
  end
55
55
  end
56
56
  end
57
-
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dynamoid
4
+ # @private
4
5
  module AdapterPlugin
5
6
  class AwsSdkV3
6
7
  module Middleware
@@ -18,11 +19,10 @@ module Dynamoid
18
19
  throw :stop_pagination
19
20
  end
20
21
 
21
- return response
22
+ response
22
23
  end
23
24
  end
24
25
  end
25
26
  end
26
27
  end
27
28
  end
28
-
@@ -5,12 +5,14 @@ require_relative 'middleware/limit'
5
5
  require_relative 'middleware/start_key'
6
6
 
7
7
  module Dynamoid
8
+ # @private
8
9
  module AdapterPlugin
9
10
  class AwsSdkV3
10
11
  class Query
11
12
  OPTIONS_KEYS = %i[
12
13
  limit hash_key hash_value range_key consistent_read scan_index_forward
13
14
  select index_name batch_size exclusive_start_key record_limit scan_limit
15
+ project
14
16
  ].freeze
15
17
 
16
18
  attr_reader :client, :table, :options, :conditions
@@ -28,8 +30,8 @@ module Dynamoid
28
30
  request = build_request
29
31
 
30
32
  Enumerator.new do |yielder|
31
- api_call = -> (request) do
32
- client.query(request).tap do |response|
33
+ api_call = lambda do |req|
34
+ client.query(req).tap do |response|
33
35
  yielder << response
34
36
  end
35
37
  end
@@ -63,10 +65,11 @@ module Dynamoid
63
65
  batch_size = options[:batch_size]
64
66
  limit = [record_limit, scan_limit, batch_size].compact.min
65
67
 
66
- request[:limit] = limit if limit
67
- request[:table_name] = table.name
68
- request[:key_conditions] = key_conditions
69
- request[:query_filter] = query_filter
68
+ request[:limit] = limit if limit
69
+ request[:table_name] = table.name
70
+ request[:key_conditions] = key_conditions
71
+ request[:query_filter] = query_filter
72
+ request[:attributes_to_get] = attributes_to_get
70
73
 
71
74
  request
72
75
  end
@@ -117,6 +120,12 @@ module Dynamoid
117
120
  result
118
121
  end
119
122
  end
123
+
124
+ def attributes_to_get
125
+ return if options[:project].nil?
126
+
127
+ options[:project].map(&:to_s)
128
+ end
120
129
  end
121
130
  end
122
131
  end
@@ -5,6 +5,7 @@ require_relative 'middleware/limit'
5
5
  require_relative 'middleware/start_key'
6
6
 
7
7
  module Dynamoid
8
+ # @private
8
9
  module AdapterPlugin
9
10
  class AwsSdkV3
10
11
  class Scan
@@ -21,8 +22,8 @@ module Dynamoid
21
22
  request = build_request
22
23
 
23
24
  Enumerator.new do |yielder|
24
- api_call = -> (request) do
25
- client.scan(request).tap do |response|
25
+ api_call = lambda do |req|
26
+ client.scan(req).tap do |response|
26
27
  yielder << response
27
28
  end
28
29
  end
@@ -54,9 +55,10 @@ module Dynamoid
54
55
  batch_size = options[:batch_size]
55
56
  limit = [record_limit, scan_limit, batch_size].compact.min
56
57
 
57
- request[:limit] = limit if limit
58
- request[:table_name] = table.name
59
- request[:scan_filter] = scan_filter
58
+ request[:limit] = limit if limit
59
+ request[:table_name] = table.name
60
+ request[:scan_filter] = scan_filter
61
+ request[:attributes_to_get] = attributes_to_get
60
62
 
61
63
  request
62
64
  end
@@ -75,10 +77,18 @@ module Dynamoid
75
77
  comparison_operator: AwsSdkV3::FIELD_MAP[cond.keys[0]],
76
78
  attribute_value_list: AwsSdkV3.attribute_value_list(AwsSdkV3::FIELD_MAP[cond.keys[0]], cond.values[0].freeze)
77
79
  }
80
+ # nil means operator doesn't require attribute value list
81
+ conditions.delete(:attribute_value_list) if conditions[:attribute_value_list].nil?
78
82
  result[attr] = condition
79
83
  result
80
84
  end
81
85
  end
86
+
87
+ def attributes_to_get
88
+ return if options[:project].nil?
89
+
90
+ options[:project].map(&:to_s)
91
+ end
82
92
  end
83
93
  end
84
94
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dynamoid
4
+ # @private
4
5
  module AdapterPlugin
5
6
  class AwsSdkV3
6
7
  # Represents a table. Exposes data from the "DescribeTable" API call, and also
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dynamoid
4
+ # @private
4
5
  module AdapterPlugin
5
6
  class AwsSdkV3
6
7
  class UntilPastTableStatus
7
- attr_reader :table_name, :status
8
+ attr_reader :client, :table_name, :status
8
9
 
9
- def initialize(table_name, status = :creating)
10
+ def initialize(client, table_name, status = :creating)
11
+ @client = client
10
12
  @table_name = table_name
11
13
  @status = status
12
14
  end
@@ -31,7 +33,7 @@ module Dynamoid
31
33
  # See: http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#describe_table-instance_method
32
34
  rescue Aws::DynamoDB::Errors::ResourceNotFoundException => e
33
35
  case status
34
- when :creating then
36
+ when :creating
35
37
  if counter >= Dynamoid::Config.sync_retry_max_times
36
38
  Dynamoid.logger.warn "Waiting on table metadata for #{table_name} (check #{counter})"
37
39
  retry # start over at first line of begin, does not reset counter
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dynamoid
4
+ # @private
4
5
  module ApplicationTimeZone
5
6
  def self.at(value)
6
7
  case Dynamoid::Config.application_timezone
@@ -25,12 +25,55 @@ module Dynamoid
25
25
  end
26
26
 
27
27
  module ClassMethods
28
- # create a has_many association for this document.
28
+ # Declare a +has_many+ association for this document.
29
29
  #
30
- # @param [Symbol] name the name of the association
31
- # @param [Hash] options options to pass to the association constructor
30
+ # class Category
31
+ # include Dynamoid::Document
32
+ #
33
+ # has_many :posts
34
+ # end
35
+ #
36
+ # Association is an enumerable collection and supports following addition
37
+ # operations:
38
+ #
39
+ # * +create+
40
+ # * +create!+
41
+ # * +destroy_all+
42
+ # * +delete_all+
43
+ # * +delete+
44
+ # * +<<+
45
+ # * +where+
46
+ # * +all+
47
+ # * +empty?+
48
+ # * +size+
49
+ #
50
+ # When a name of an associated class doesn't match an association name a
51
+ # class name should be specified explicitly either with +class+ or
52
+ # +class_name+ option:
53
+ #
54
+ # has_many :labels, class: Tag
55
+ # has_many :labels, class_name: 'Tag'
56
+ #
57
+ # When associated class has own +belongs_to+ association to
58
+ # the current class and the name doesn't match a name of the current
59
+ # class this name can be specified with +inverse_of+ option:
60
+ #
61
+ # class Post
62
+ # include Dynamoid::Document
63
+ #
64
+ # belongs_to :item, class_name: 'Tag'
65
+ # end
66
+ #
67
+ # class Tag
68
+ # include Dynamoid::Document
69
+ #
70
+ # has_many :posts, inverse_of: :item
71
+ # end
72
+ #
73
+ # @param name [Symbol] the name of the association
74
+ # @param options [Hash] options to pass to the association constructor
32
75
  # @option options [Class] :class the target class of the has_many association; that is, the belongs_to class
33
- # @option options [Symbol] :class_name the name of the target class of the association; that is, the name of the belongs_to class
76
+ # @option options [String] :class_name the name of the target class of the association; that is, the name of the belongs_to class
34
77
  # @option options [Symbol] :inverse_of the name of the association on the target class; that is, if the class has a belongs_to association, the name of that association
35
78
  #
36
79
  # @since 0.2.0
@@ -38,12 +81,47 @@ module Dynamoid
38
81
  association(:has_many, name, options)
39
82
  end
40
83
 
41
- # create a has_one association for this document.
84
+ # Declare a +has_one+ association for this document.
85
+ #
86
+ # class Image
87
+ # include Dynamoid::Document
42
88
  #
43
- # @param [Symbol] name the name of the association
44
- # @param [Hash] options options to pass to the association constructor
89
+ # has_one :post
90
+ # end
91
+ #
92
+ # Association supports following operations:
93
+ #
94
+ # * +create+
95
+ # * +create!+
96
+ # * +delete+
97
+ #
98
+ # When a name of an associated class doesn't match an association name a
99
+ # class name should be specified explicitly either with +class+ or
100
+ # +class_name+ option:
101
+ #
102
+ # has_one :item, class: Post
103
+ # has_one :item, class_name: 'Post'
104
+ #
105
+ # When associated class has own +belong_to+ association to the current
106
+ # class and the name doesn't match a name of the current class this name
107
+ # can be specified with +inverse_of+ option:
108
+ #
109
+ # class Post
110
+ # include Dynamoid::Document
111
+ #
112
+ # belongs_to :logo, class_name: 'Image'
113
+ # end
114
+ #
115
+ # class Image
116
+ # include Dynamoid::Document
117
+ #
118
+ # has_one :post, inverse_of: :logo
119
+ # end
120
+ #
121
+ # @param name [Symbol] the name of the association
122
+ # @param options [Hash] options to pass to the association constructor
45
123
  # @option options [Class] :class the target class of the has_one association; that is, the belongs_to class
46
- # @option options [Symbol] :class_name the name of the target class of the association; that is, the name of the belongs_to class
124
+ # @option options [String] :class_name the name of the target class of the association; that is, the name of the belongs_to class
47
125
  # @option options [Symbol] :inverse_of the name of the association on the target class; that is, if the class has a belongs_to association, the name of that association
48
126
  #
49
127
  # @since 0.2.0
@@ -51,25 +129,110 @@ module Dynamoid
51
129
  association(:has_one, name, options)
52
130
  end
53
131
 
54
- # create a belongs_to association for this document.
132
+ # Declare a +belongs_to+ association for this document.
133
+ #
134
+ # class Post
135
+ # include Dynamoid::Document
55
136
  #
56
- # @param [Symbol] name the name of the association
57
- # @param [Hash] options options to pass to the association constructor
137
+ # belongs_to :categories
138
+ # end
139
+ #
140
+ # Association supports following operations:
141
+ #
142
+ # * +create+
143
+ # * +create!+
144
+ # * +delete+
145
+ #
146
+ # When a name of an associated class doesn't match an association name a
147
+ # class name should be specified explicitly either with +class+ or
148
+ # +class_name+ option:
149
+ #
150
+ # belongs_to :item, class: Post
151
+ # belongs_to :item, class_name: 'Post'
152
+ #
153
+ # When associated class has own +has_many+ or +has_one+ association to
154
+ # the current class and the name doesn't match a name of the current
155
+ # class this name can be specified with +inverse_of+ option:
156
+ #
157
+ # class Category
158
+ # include Dynamoid::Document
159
+ #
160
+ # has_many :items, class_name: 'Post'
161
+ # end
162
+ #
163
+ # class Post
164
+ # include Dynamoid::Document
165
+ #
166
+ # belongs_to :categories, inverse_of: :items
167
+ # end
168
+ #
169
+ # By default a hash key attribute name is +id+. If an associated class
170
+ # uses another name for a hash key attribute it should be specified in
171
+ # the +belongs_to+ association:
172
+ #
173
+ # belongs_to :categories, foreign_key: :uuid
174
+ #
175
+ # @param name [Symbol] the name of the association
176
+ # @param options [Hash] options to pass to the association constructor
58
177
  # @option options [Class] :class the target class of the has_one association; that is, the has_many or has_one class
59
- # @option options [Symbol] :class_name the name of the target class of the association; that is, the name of the has_many or has_one class
178
+ # @option options [String] :class_name the name of the target class of the association; that is, the name of the has_many or has_one class
60
179
  # @option options [Symbol] :inverse_of the name of the association on the target class; that is, if the class has a has_many or has_one association, the name of that association
180
+ # @option options [Symbol] :foreign_key the name of a hash key attribute in the target class
61
181
  #
62
182
  # @since 0.2.0
63
183
  def belongs_to(name, options = {})
64
184
  association(:belongs_to, name, options)
65
185
  end
66
186
 
67
- # create a has_and_belongs_to_many association for this document.
187
+ # Declare a +has_and_belongs_to_many+ association for this document.
188
+ #
189
+ # class Post
190
+ # include Dynamoid::Document
191
+ #
192
+ # has_and_belongs_to_many :tags
193
+ # end
194
+ #
195
+ # Association is an enumerable collection and supports following addition
196
+ # operations:
197
+ #
198
+ # * +create+
199
+ # * +create!+
200
+ # * +destroy_all+
201
+ # * +delete_all+
202
+ # * +delete+
203
+ # * +<<+
204
+ # * +where+
205
+ # * +all+
206
+ # * +empty?+
207
+ # * +size+
208
+ #
209
+ # When a name of an associated class doesn't match an association name a
210
+ # class name should be specified explicitly either with +class+ or
211
+ # +class_name+ option:
212
+ #
213
+ # has_and_belongs_to_many :labels, class: Tag
214
+ # has_and_belongs_to_many :labels, class_name: 'Tag'
215
+ #
216
+ # When associated class has own +has_and_belongs_to_many+ association to
217
+ # the current class and the name doesn't match a name of the current
218
+ # class this name can be specified with +inverse_of+ option:
219
+ #
220
+ # class Tag
221
+ # include Dynamoid::Document
222
+ #
223
+ # has_and_belongs_to_many :items, class_name: 'Post'
224
+ # end
225
+ #
226
+ # class Post
227
+ # include Dynamoid::Document
228
+ #
229
+ # has_and_belongs_to_many :tags, inverse_of: :items
230
+ # end
68
231
  #
69
- # @param [Symbol] name the name of the association
70
- # @param [Hash] options options to pass to the association constructor
232
+ # @param name [Symbol] the name of the association
233
+ # @param options [Hash] options to pass to the association constructor
71
234
  # @option options [Class] :class the target class of the has_and_belongs_to_many association; that is, the belongs_to class
72
- # @option options [Symbol] :class_name the name of the target class of the association; that is, the name of the belongs_to class
235
+ # @option options [String] :class_name the name of the target class of the association; that is, the name of the belongs_to class
73
236
  # @option options [Symbol] :inverse_of the name of the association on the target class; that is, if the class has a belongs_to association, the name of that association
74
237
  #
75
238
  # @since 0.2.0
@@ -81,9 +244,9 @@ module Dynamoid
81
244
 
82
245
  # create getters and setters for an association.
83
246
  #
84
- # @param [Symbol] symbol the type (:has_one, :has_many, :has_and_belongs_to_many, :belongs_to) of the association
85
- # @param [Symbol] name the name of the association
86
- # @param [Hash] options options to pass to the association constructor; see above for all valid options
247
+ # @param type [Symbol] the type (:has_one, :has_many, :has_and_belongs_to_many, :belongs_to) of the association
248
+ # @param name [Symbol] the name of the association
249
+ # @param options [Hash] options to pass to the association constructor; see above for all valid options
87
250
  #
88
251
  # @since 0.2.0
89
252
  def association(type, name, options = {})