dynamoid 3.6.0 → 3.7.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 +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +28 -8
- data/lib/dynamoid/adapter.rb +7 -1
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +10 -1
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb +2 -2
- data/lib/dynamoid/associations/association.rb +6 -0
- data/lib/dynamoid/associations/many_association.rb +3 -1
- data/lib/dynamoid/associations/single_association.rb +3 -3
- data/lib/dynamoid/criteria.rb +8 -1
- data/lib/dynamoid/criteria/chain.rb +31 -1
- data/lib/dynamoid/criteria/key_fields_detector.rb +14 -1
- data/lib/dynamoid/errors.rb +2 -0
- data/lib/dynamoid/fields.rb +29 -35
- data/lib/dynamoid/fields/declare.rb +86 -0
- data/lib/dynamoid/indexes.rb +10 -0
- data/lib/dynamoid/loadable.rb +2 -2
- data/lib/dynamoid/persistence.rb +23 -3
- data/lib/dynamoid/persistence/update_fields.rb +4 -2
- data/lib/dynamoid/persistence/update_validations.rb +18 -0
- data/lib/dynamoid/persistence/upsert.rb +4 -2
- data/lib/dynamoid/validations.rb +4 -1
- data/lib/dynamoid/version.rb +1 -1
- metadata +11 -9
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 2146a1a188ecf2b8dd2f280172119879d6612d9eadda6cbcbf6defb3319f7022
         | 
| 4 | 
            +
              data.tar.gz: df59300783324be7952fd25f8d49880707938762d3960ce1d422c18b49e26ac6
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: d2ebd06729d024b98338e5c11953976935f5252ad76f6dec9265929fd447ed488295d9f06c8ae5440c7c6a494fb1cdb44ffeba002c18ea52bd4ad100df2d4c66
         | 
| 7 | 
            +
              data.tar.gz: eddf03eb98125fbc7fcfa842e8276ba5dcbd558abacc3adeb2d451e3bb71bcb92f69eb84a01231304b4690b087a826ebf7c4ee2426a16d92a7a126ceddf1e132
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,11 +1,28 @@ | |
| 1 1 | 
             
            # HEAD
         | 
| 2 2 |  | 
| 3 | 
            +
            ---
         | 
| 4 | 
            +
             | 
| 5 | 
            +
             | 
| 6 | 
            +
             | 
| 7 | 
            +
            # 3.7.0 / 2021-02-02
         | 
| 8 | 
            +
             | 
| 9 | 
            +
             | 
| 3 10 | 
             
            ## Features
         | 
| 4 11 |  | 
| 12 | 
            +
            * [#476](https://github.com/Dynamoid/dynamoid/pull/476) Added `#with_index` method to force an index in query (@bmalinconico)
         | 
| 13 | 
            +
            * [#481](https://github.com/Dynamoid/dynamoid/pull/481) Added `alias` option to the `field` method to declare a field alias and use more conventional name to set and get value
         | 
| 14 | 
            +
             | 
| 5 15 | 
             
            ## Improvements
         | 
| 6 16 |  | 
| 17 | 
            +
            * [#482](https://github.com/Dynamoid/dynamoid/pull/482) Support Ruby 3.0 and Rails 6.1
         | 
| 18 | 
            +
            * [#461](https://github.com/Dynamoid/dynamoid/pull/461) Allow to delete item attribute with `#update` method (@jkirsteins)
         | 
| 19 | 
            +
            * [#463](https://github.com/Dynamoid/dynamoid/pull/463) Raise `UnknownAttribute` exception when specified not declared attribute name (@AlexGascon)
         | 
| 20 | 
            +
             | 
| 7 21 | 
             
            ## Fixes
         | 
| 8 22 |  | 
| 23 | 
            +
            * [#480](https://github.com/Dynamoid/dynamoid/pull/480) Repair `.consistent`/`.delete_all`/`.destroy_all` calls directly on a model class
         | 
| 24 | 
            +
            * [#484](https://github.com/Dynamoid/dynamoid/pull/484) Fix broken foreign keys after model deleting (@kkan)
         | 
| 25 | 
            +
            * Fixes in Readme.md: [#470](https://github.com/Dynamoid/dynamoid/pull/470) (@rromanchuk), [#473](https://github.com/Dynamoid/dynamoid/pull/473) (@Rulikkk)
         | 
| 9 26 |  | 
| 10 27 | 
             
            ---
         | 
| 11 28 |  | 
    
        data/README.md
    CHANGED
    
    | @@ -126,8 +126,8 @@ end | |
| 126 126 | 
             
            Dynamoid supports Ruby >= 2.3 and Rails >= 4.2.
         | 
| 127 127 |  | 
| 128 128 | 
             
            Its compatibility is tested against following Ruby versions: 2.3, 2.4,
         | 
| 129 | 
            -
            2.5  | 
| 130 | 
            -
            5.2 and 6. | 
| 129 | 
            +
            2.5, 2.6, 2.7 and 3.0, JRuby 9.2.x and against Rails versions: 4.2, 5.0, 5.1,
         | 
| 130 | 
            +
            5.2, 6.0 and 6.1.
         | 
| 131 131 |  | 
| 132 132 | 
             
            ## Setup
         | 
| 133 133 |  | 
| @@ -365,6 +365,26 @@ field :actions_taken, :integer, default: 0 | |
| 365 365 | 
             
            field :joined_at, :datetime, default: -> { Time.now }
         | 
| 366 366 | 
             
            ```
         | 
| 367 367 |  | 
| 368 | 
            +
            #### Aliases
         | 
| 369 | 
            +
             | 
| 370 | 
            +
            It might be helpful to define an alias for already existing field when
         | 
| 371 | 
            +
            naming convention used for a table differs from conventions common in
         | 
| 372 | 
            +
            Ruby:
         | 
| 373 | 
            +
             | 
| 374 | 
            +
            ```ruby
         | 
| 375 | 
            +
            field firstName, :string, alias: :first_name
         | 
| 376 | 
            +
            ```
         | 
| 377 | 
            +
             | 
| 378 | 
            +
            This way there will be generated
         | 
| 379 | 
            +
            setters/getters/`<name>?`/`<name>_before_type_cast` methods for both
         | 
| 380 | 
            +
            original field name (`firstName`) and an alias (`first_name`).
         | 
| 381 | 
            +
             | 
| 382 | 
            +
            ```ruby
         | 
| 383 | 
            +
            user = User.new(first_name: 'Michael')
         | 
| 384 | 
            +
            user.first_name # => 'Michael'
         | 
| 385 | 
            +
            user.firstName # => 'Michael'
         | 
| 386 | 
            +
            ```
         | 
| 387 | 
            +
             | 
| 368 388 | 
             
            #### Custom Types
         | 
| 369 389 |  | 
| 370 390 | 
             
            To use a custom type for a field, suppose you have a `Money` type.
         | 
| @@ -576,7 +596,7 @@ c.my_new_type | |
| 576 596 |  | 
| 577 597 | 
             
            ### Type casting
         | 
| 578 598 |  | 
| 579 | 
            -
             | 
| 599 | 
            +
            Dynamoid supports type casting and tries to do it in the most convenient
         | 
| 580 600 | 
             
            way. Values for all fields (except custom type) are coerced to declared
         | 
| 581 601 | 
             
            field types.
         | 
| 582 602 |  | 
| @@ -614,7 +634,7 @@ well. | |
| 614 634 |  | 
| 615 635 | 
             
            ### Dirty API
         | 
| 616 636 |  | 
| 617 | 
            -
            Dynamoid supports Dirty API which  | 
| 637 | 
            +
            Dynamoid supports Dirty API which is equivalent to [Rails 5.2
         | 
| 618 638 | 
             
            `ActiveModel::Dirty`](https://api.rubyonrails.org/v5.2/classes/ActiveModel/Dirty.html).
         | 
| 619 639 | 
             
            There is only one limitation - change in place of field isn't detected
         | 
| 620 640 | 
             
            automatically.
         | 
| @@ -632,7 +652,7 @@ u.email = 'josh@joshsymonds.com' | |
| 632 652 | 
             
            u.save
         | 
| 633 653 | 
             
            ```
         | 
| 634 654 |  | 
| 635 | 
            -
            Save forces persistence to the  | 
| 655 | 
            +
            Save forces persistence to the data store: a unique ID is also assigned,
         | 
| 636 656 | 
             
            but it is a string and not an auto-incrementing number.
         | 
| 637 657 |  | 
| 638 658 | 
             
            ```ruby
         | 
| @@ -1267,7 +1287,7 @@ class User | |
| 1267 1287 | 
             
            end
         | 
| 1268 1288 |  | 
| 1269 1289 | 
             
            Dynamoid.config.logger.level = :debug
         | 
| 1270 | 
            -
            Dynamoid.config.endpoint = 'localhost:8000'
         | 
| 1290 | 
            +
            Dynamoid.config.endpoint = 'http://localhost:8000'
         | 
| 1271 1291 |  | 
| 1272 1292 | 
             
            User.create(name: 'Alex')
         | 
| 1273 1293 |  | 
| @@ -1342,12 +1362,12 @@ environment. | |
| 1342 1362 |  | 
| 1343 1363 | 
             
            If you want to run all the specs that travis runs, use `bundle exec
         | 
| 1344 1364 | 
             
            wwtd`, but first you will need to setup all the rubies, for each of `%w(
         | 
| 1345 | 
            -
            2. | 
| 1365 | 
            +
            2.3.8 2.4.6 2.5.5 2.6.3 2.7.0 3.0.0 9.2.14.0)`. When you run
         | 
| 1346 1366 | 
             
            `bundle exec wwtd` it will take care of starting and stopping the local
         | 
| 1347 1367 | 
             
            dynamodb instance.
         | 
| 1348 1368 |  | 
| 1349 1369 | 
             
            ```shell
         | 
| 1350 | 
            -
            rvm use  | 
| 1370 | 
            +
            rvm use 3.0.0
         | 
| 1351 1371 | 
             
            gem install rubygems-update
         | 
| 1352 1372 | 
             
            gem install bundler
         | 
| 1353 1373 | 
             
            bundle install
         | 
    
        data/lib/dynamoid/adapter.rb
    CHANGED
    
    | @@ -158,8 +158,14 @@ module Dynamoid | |
| 158 158 | 
             
                #
         | 
| 159 159 | 
             
                # @since 0.2.0
         | 
| 160 160 | 
             
                def method_missing(method, *args, &block)
         | 
| 161 | 
            -
                   | 
| 161 | 
            +
                  # Don't use keywork arguments delegating (with **kw). It works in
         | 
| 162 | 
            +
                  # different way in different Ruby versions: <= 2.6, 2.7, 3.0 and in some
         | 
| 163 | 
            +
                  # future 3.x versions. Providing that there are no downstream methods
         | 
| 164 | 
            +
                  # with keyword arguments in adapter.
         | 
| 165 | 
            +
                  #
         | 
| 166 | 
            +
                  # https://eregon.me/blog/2019/11/10/the-delegation-challenge-of-ruby27.html
         | 
| 162 167 |  | 
| 168 | 
            +
                  return benchmark(method, *args) { adapter.send(method, *args, &block) } if adapter.respond_to?(method)
         | 
| 163 169 | 
             
                  super
         | 
| 164 170 | 
             
                end
         | 
| 165 171 |  | 
| @@ -12,6 +12,15 @@ module Dynamoid | |
| 12 12 | 
             
              # @private
         | 
| 13 13 | 
             
              module AdapterPlugin
         | 
| 14 14 | 
             
                # The AwsSdkV3 adapter provides support for the aws-sdk version 2 for ruby.
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                # Note: Don't use keyword arguments in public methods as far as method
         | 
| 17 | 
            +
                # calls on adapter are delegated to the plugin.
         | 
| 18 | 
            +
                #
         | 
| 19 | 
            +
                # There are breaking changes in Ruby related to delegating keyword
         | 
| 20 | 
            +
                # arguments so we have decided just to avoid them when use delegation.
         | 
| 21 | 
            +
                #
         | 
| 22 | 
            +
                # https://eregon.me/blog/2019/11/10/the-delegation-challenge-of-ruby27.html
         | 
| 23 | 
            +
             | 
| 15 24 | 
             
                class AwsSdkV3
         | 
| 16 25 | 
             
                  EQ = 'EQ'
         | 
| 17 26 | 
             
                  RANGE_MAP = {
         | 
| @@ -281,7 +290,7 @@ module Dynamoid | |
| 281 290 | 
             
                    false
         | 
| 282 291 | 
             
                  end
         | 
| 283 292 |  | 
| 284 | 
            -
                  def update_time_to_live(table_name | 
| 293 | 
            +
                  def update_time_to_live(table_name, attribute)
         | 
| 285 294 | 
             
                    request = {
         | 
| 286 295 | 
             
                      table_name: table_name,
         | 
| 287 296 | 
             
                      time_to_live_specification: {
         | 
| @@ -58,6 +58,12 @@ module Dynamoid | |
| 58 58 | 
             
                    :set
         | 
| 59 59 | 
             
                  end
         | 
| 60 60 |  | 
| 61 | 
            +
                  def disassociate_source
         | 
| 62 | 
            +
                    Array(target).each do |target_entry|
         | 
| 63 | 
            +
                      target_entry.send(target_association).disassociate(source.hash_key) if target_entry && target_association
         | 
| 64 | 
            +
                    end
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
             | 
| 61 67 | 
             
                  private
         | 
| 62 68 |  | 
| 63 69 | 
             
                  # The target class name, either inferred through the association's name or specified in options.
         | 
| @@ -28,7 +28,7 @@ module Dynamoid | |
| 28 28 | 
             
                  # unsaved changes will be saved. Doesn't delete an associated model from
         | 
| 29 29 | 
             
                  # DynamoDB.
         | 
| 30 30 | 
             
                  def delete
         | 
| 31 | 
            -
                     | 
| 31 | 
            +
                    disassociate_source
         | 
| 32 32 | 
             
                    disassociate
         | 
| 33 33 | 
             
                    target
         | 
| 34 34 | 
             
                  end
         | 
| @@ -90,7 +90,7 @@ module Dynamoid | |
| 90 90 |  | 
| 91 91 | 
             
                  # @private
         | 
| 92 92 | 
             
                  def associate(hash_key)
         | 
| 93 | 
            -
                     | 
| 93 | 
            +
                    disassociate_source
         | 
| 94 94 | 
             
                    source.update_attribute(source_attribute, Set[hash_key])
         | 
| 95 95 | 
             
                  end
         | 
| 96 96 |  | 
| @@ -109,7 +109,7 @@ module Dynamoid | |
| 109 109 | 
             
                  def find_target
         | 
| 110 110 | 
             
                    return if source_ids.empty?
         | 
| 111 111 |  | 
| 112 | 
            -
                    target_class.find(source_ids.first)
         | 
| 112 | 
            +
                    target_class.find(source_ids.first, raise_error: false)
         | 
| 113 113 | 
             
                  end
         | 
| 114 114 |  | 
| 115 115 | 
             
                  def target=(object)
         | 
    
        data/lib/dynamoid/criteria.rb
    CHANGED
    
    | @@ -9,12 +9,19 @@ module Dynamoid | |
| 9 9 |  | 
| 10 10 | 
             
                # @private
         | 
| 11 11 | 
             
                module ClassMethods
         | 
| 12 | 
            -
                  %i[where all first last each record_limit scan_limit batch start scan_index_forward find_by_pages project pluck].each do |meth|
         | 
| 12 | 
            +
                  %i[where consistent all first last delete_all destroy_all each record_limit scan_limit batch start scan_index_forward find_by_pages project pluck].each do |meth|
         | 
| 13 13 | 
             
                    # Return a criteria chain in response to a method that will begin or end a chain. For more information,
         | 
| 14 14 | 
             
                    # see Dynamoid::Criteria::Chain.
         | 
| 15 15 | 
             
                    #
         | 
| 16 16 | 
             
                    # @since 0.2.0
         | 
| 17 17 | 
             
                    define_method(meth) do |*args, &blk|
         | 
| 18 | 
            +
                      # Don't use keywork arguments delegating (with **kw). It works in
         | 
| 19 | 
            +
                      # different way in different Ruby versions: <= 2.6, 2.7, 3.0 and in some
         | 
| 20 | 
            +
                      # future 3.x versions. Providing that there are no downstream methods
         | 
| 21 | 
            +
                      # with keyword arguments in Chain.
         | 
| 22 | 
            +
                      #
         | 
| 23 | 
            +
                      # https://eregon.me/blog/2019/11/10/the-delegation-challenge-of-ruby27.html
         | 
| 24 | 
            +
             | 
| 18 25 | 
             
                      chain = Dynamoid::Criteria::Chain.new(self)
         | 
| 19 26 | 
             
                      if args
         | 
| 20 27 | 
             
                        chain.send(meth, *args, &blk)
         | 
| @@ -110,7 +110,7 @@ module Dynamoid | |
| 110 110 | 
             
                    query.update(args.symbolize_keys)
         | 
| 111 111 |  | 
| 112 112 | 
             
                    # we should re-initialize keys detector every time we change query
         | 
| 113 | 
            -
                    @key_fields_detector = KeyFieldsDetector.new(@query, @source)
         | 
| 113 | 
            +
                    @key_fields_detector = KeyFieldsDetector.new(@query, @source, forced_index_name: @forced_index_name)
         | 
| 114 114 |  | 
| 115 115 | 
             
                    self
         | 
| 116 116 | 
             
                  end
         | 
| @@ -358,6 +358,36 @@ module Dynamoid | |
| 358 358 | 
             
                    self
         | 
| 359 359 | 
             
                  end
         | 
| 360 360 |  | 
| 361 | 
            +
                  # Force the index name to use for queries.
         | 
| 362 | 
            +
                  #
         | 
| 363 | 
            +
                  # By default allows the library to select the most appropriate index.
         | 
| 364 | 
            +
                  # Sometimes you have more than one index which will fulfill your query's
         | 
| 365 | 
            +
                  # needs. When this case occurs you may want to force an order. This occurs
         | 
| 366 | 
            +
                  # when you are searching by hash key, but not specifying a range key.
         | 
| 367 | 
            +
                  #
         | 
| 368 | 
            +
                  #  class Comment
         | 
| 369 | 
            +
                  #    include Dynamoid::Document
         | 
| 370 | 
            +
                  #
         | 
| 371 | 
            +
                  #    table key: :post_id
         | 
| 372 | 
            +
                  #    range_key :author_id
         | 
| 373 | 
            +
                  #
         | 
| 374 | 
            +
                  #    field :post_date, :datetime
         | 
| 375 | 
            +
                  #
         | 
| 376 | 
            +
                  #    global_secondary_index name: :time_sorted_comments, hash_key: :post_id, range_key: post_date, projected_attributes: :all
         | 
| 377 | 
            +
                  #  end
         | 
| 378 | 
            +
                  #
         | 
| 379 | 
            +
                  #
         | 
| 380 | 
            +
                  #   Comment.where(post_id: id).with_index(:time_sorted_comments).scan_index_forward(false)
         | 
| 381 | 
            +
                  #
         | 
| 382 | 
            +
                  # @return [Dynamoid::Criteria::Chain]
         | 
| 383 | 
            +
                  def with_index(index_name)
         | 
| 384 | 
            +
                    raise Dynamoid::Errors::InvalidIndex, "Unknown index #{index_name}" unless @source.find_index_by_name(index_name)
         | 
| 385 | 
            +
             | 
| 386 | 
            +
                    @forced_index_name = index_name
         | 
| 387 | 
            +
                    @key_fields_detector = KeyFieldsDetector.new(@query, @source, forced_index_name: index_name)
         | 
| 388 | 
            +
                    self
         | 
| 389 | 
            +
                  end
         | 
| 390 | 
            +
             | 
| 361 391 | 
             
                  # Allows to use the results of a search as an enumerable over the results
         | 
| 362 392 | 
             
                  # found.
         | 
| 363 393 | 
             
                  #
         | 
| @@ -24,10 +24,11 @@ module Dynamoid | |
| 24 24 | 
             
                    end
         | 
| 25 25 | 
             
                  end
         | 
| 26 26 |  | 
| 27 | 
            -
                  def initialize(query, source)
         | 
| 27 | 
            +
                  def initialize(query, source, forced_index_name: nil)
         | 
| 28 28 | 
             
                    @query = query
         | 
| 29 29 | 
             
                    @source = source
         | 
| 30 30 | 
             
                    @query = Query.new(query)
         | 
| 31 | 
            +
                    @forced_index_name = forced_index_name
         | 
| 31 32 | 
             
                    @result = find_keys_in_query
         | 
| 32 33 | 
             
                  end
         | 
| 33 34 |  | 
| @@ -54,6 +55,8 @@ module Dynamoid | |
| 54 55 | 
             
                  private
         | 
| 55 56 |  | 
| 56 57 | 
             
                  def find_keys_in_query
         | 
| 58 | 
            +
                    return match_forced_index if @forced_index_name
         | 
| 59 | 
            +
             | 
| 57 60 | 
             
                    match_table_and_sort_key ||
         | 
| 58 61 | 
             
                      match_local_secondary_index ||
         | 
| 59 62 | 
             
                      match_global_secondary_index_and_sort_key ||
         | 
| @@ -133,6 +136,16 @@ module Dynamoid | |
| 133 136 | 
             
                      }
         | 
| 134 137 | 
             
                    end
         | 
| 135 138 | 
             
                  end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                  def match_forced_index
         | 
| 141 | 
            +
                    idx = @source.find_index_by_name(@forced_index_name)
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                    {
         | 
| 144 | 
            +
                      hash_key: idx.hash_key,
         | 
| 145 | 
            +
                      range_key: idx.range_key,
         | 
| 146 | 
            +
                      index_name: idx.name,
         | 
| 147 | 
            +
                    }
         | 
| 148 | 
            +
                  end
         | 
| 136 149 | 
             
                end
         | 
| 137 150 | 
             
              end
         | 
| 138 151 | 
             
            end
         | 
    
        data/lib/dynamoid/errors.rb
    CHANGED
    
    
    
        data/lib/dynamoid/fields.rb
    CHANGED
    
    | @@ -1,5 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require 'dynamoid/fields/declare'
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            module Dynamoid
         | 
| 4 6 | 
             
              # All fields on a Dynamoid::Document must be explicitly defined -- if you have fields in the database that are not
         | 
| 5 7 | 
             
              # specified with field, then they will be ignored.
         | 
| @@ -43,15 +45,15 @@ module Dynamoid | |
| 43 45 | 
             
                  #   end
         | 
| 44 46 | 
             
                  #
         | 
| 45 47 | 
             
                  # Its type determines how it is coerced when read in and out of the
         | 
| 46 | 
            -
                  #  | 
| 48 | 
            +
                  # data store. You can specify +string+, +integer+, +number+, +set+, +array+,
         | 
| 47 49 | 
             
                  # +map+, +datetime+, +date+, +serialized+, +raw+, +boolean+ and +binary+
         | 
| 48 50 | 
             
                  # or specify a class that defines a serialization strategy.
         | 
| 49 51 | 
             
                  #
         | 
| 50 52 | 
             
                  # By default field type is +string+.
         | 
| 51 53 | 
             
                  #
         | 
| 52 54 | 
             
                  # Set can store elements of the same type only (it's a limitation of
         | 
| 53 | 
            -
                  # DynamoDB itself). If a set should store elements only some particular
         | 
| 54 | 
            -
                  # type +of+ option should be specified:
         | 
| 55 | 
            +
                  # DynamoDB itself). If a set should store elements only of some particular
         | 
| 56 | 
            +
                  # type then +of+ option should be specified:
         | 
| 55 57 | 
             
                  #
         | 
| 56 58 | 
             
                  #   field :hobbies, :set, of: :string
         | 
| 57 59 | 
             
                  #
         | 
| @@ -126,41 +128,31 @@ module Dynamoid | |
| 126 128 | 
             
                  #   user.age # => 21 - integer
         | 
| 127 129 | 
             
                  #   user.age_before_type_cast # => '21' - string
         | 
| 128 130 | 
             
                  #
         | 
| 131 | 
            +
                  # There is also an option +alias+ which allows to use another name for a
         | 
| 132 | 
            +
                  # field:
         | 
| 133 | 
            +
                  #
         | 
| 134 | 
            +
                  #   class User
         | 
| 135 | 
            +
                  #     include Dynamoid::Document
         | 
| 136 | 
            +
                  #
         | 
| 137 | 
            +
                  #     field :firstName, :string, alias: :first_name
         | 
| 138 | 
            +
                  #   end
         | 
| 139 | 
            +
                  #
         | 
| 140 | 
            +
                  #   user = User.new(firstName: 'Michael')
         | 
| 141 | 
            +
                  #   user.firstName # Michael
         | 
| 142 | 
            +
                  #   user.first_name # Michael
         | 
| 143 | 
            +
                  #
         | 
| 129 144 | 
             
                  # @param name [Symbol] name of the field
         | 
| 130 145 | 
             
                  # @param type [Symbol] type of the field (optional)
         | 
| 131 146 | 
             
                  # @param options [Hash] any additional options for the field type (optional)
         | 
| 132 147 | 
             
                  #
         | 
| 133 148 | 
             
                  # @since 0.2.0
         | 
| 134 149 | 
             
                  def field(name, type = :string, options = {})
         | 
| 135 | 
            -
                    named = name.to_s
         | 
| 136 150 | 
             
                    if type == :float
         | 
| 137 151 | 
             
                      Dynamoid.logger.warn("Field type :float, which you declared for '#{name}', is deprecated in favor of :number.")
         | 
| 138 152 | 
             
                      type = :number
         | 
| 139 153 | 
             
                    end
         | 
| 140 | 
            -
                    self.attributes = attributes.merge(name => { type: type }.merge(options))
         | 
| 141 | 
            -
             | 
| 142 | 
            -
                    # should be called before `define_attribute_methods` method because it defines a getter itself
         | 
| 143 | 
            -
                    warn_about_method_overriding(name, name)
         | 
| 144 | 
            -
                    warn_about_method_overriding("#{named}=", name)
         | 
| 145 | 
            -
                    warn_about_method_overriding("#{named}?", name)
         | 
| 146 | 
            -
                    warn_about_method_overriding("#{named}_before_type_cast?", name)
         | 
| 147 154 |  | 
| 148 | 
            -
                     | 
| 149 | 
            -
             | 
| 150 | 
            -
                    generated_methods.module_eval do
         | 
| 151 | 
            -
                      define_method(named) { read_attribute(named) }
         | 
| 152 | 
            -
                      define_method("#{named}?") do
         | 
| 153 | 
            -
                        value = read_attribute(named)
         | 
| 154 | 
            -
                        case value
         | 
| 155 | 
            -
                        when true        then true
         | 
| 156 | 
            -
                        when false, nil  then false
         | 
| 157 | 
            -
                        else
         | 
| 158 | 
            -
                          !value.nil?
         | 
| 159 | 
            -
                        end
         | 
| 160 | 
            -
                      end
         | 
| 161 | 
            -
                      define_method("#{named}=") { |value| write_attribute(named, value) }
         | 
| 162 | 
            -
                      define_method("#{named}_before_type_cast") { read_attribute_before_type_cast(named) }
         | 
| 163 | 
            -
                    end
         | 
| 155 | 
            +
                    Dynamoid::Fields::Declare.new(self, name, type, options).call
         | 
| 164 156 | 
             
                  end
         | 
| 165 157 |  | 
| 166 158 | 
             
                  # Declare a table range key.
         | 
| @@ -273,8 +265,7 @@ module Dynamoid | |
| 273 265 | 
             
                    options[:timestamps] || (options[:timestamps].nil? && Dynamoid::Config.timestamps)
         | 
| 274 266 | 
             
                  end
         | 
| 275 267 |  | 
| 276 | 
            -
                  private
         | 
| 277 | 
            -
             | 
| 268 | 
            +
                  # @private
         | 
| 278 269 | 
             
                  def generated_methods
         | 
| 279 270 | 
             
                    @generated_methods ||= begin
         | 
| 280 271 | 
             
                      Module.new.tap do |mod|
         | 
| @@ -282,12 +273,6 @@ module Dynamoid | |
| 282 273 | 
             
                      end
         | 
| 283 274 | 
             
                    end
         | 
| 284 275 | 
             
                  end
         | 
| 285 | 
            -
             | 
| 286 | 
            -
                  def warn_about_method_overriding(method_name, field_name)
         | 
| 287 | 
            -
                    if instance_methods.include?(method_name.to_sym)
         | 
| 288 | 
            -
                      Dynamoid.logger.warn("Method #{method_name} generated for the field #{field_name} overrides already existing method")
         | 
| 289 | 
            -
                    end
         | 
| 290 | 
            -
                  end
         | 
| 291 276 | 
             
                end
         | 
| 292 277 |  | 
| 293 278 | 
             
                # You can access the attributes of an object directly on its attributes method, which is by default an empty hash.
         | 
| @@ -309,6 +294,10 @@ module Dynamoid | |
| 309 294 | 
             
                def write_attribute(name, value)
         | 
| 310 295 | 
             
                  name = name.to_sym
         | 
| 311 296 |  | 
| 297 | 
            +
                  unless attribute_is_present_on_model?(name)
         | 
| 298 | 
            +
                    raise Dynamoid::Errors::UnknownAttribute.new("Attribute #{name} is not part of the model")
         | 
| 299 | 
            +
                  end
         | 
| 300 | 
            +
             | 
| 312 301 | 
             
                  if association = @associations[name]
         | 
| 313 302 | 
             
                    association.reset
         | 
| 314 303 | 
             
                  end
         | 
| @@ -405,5 +394,10 @@ module Dynamoid | |
| 405 394 | 
             
                    send("#{type}=", self.class.name)
         | 
| 406 395 | 
             
                  end
         | 
| 407 396 | 
             
                end
         | 
| 397 | 
            +
             | 
| 398 | 
            +
                def attribute_is_present_on_model?(attribute_name)
         | 
| 399 | 
            +
                  setter = "#{attribute_name}=".to_sym
         | 
| 400 | 
            +
                  respond_to?(setter)
         | 
| 401 | 
            +
                end
         | 
| 408 402 | 
             
              end
         | 
| 409 403 | 
             
            end
         | 
| @@ -0,0 +1,86 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Dynamoid
         | 
| 4 | 
            +
              module Fields
         | 
| 5 | 
            +
                # @private
         | 
| 6 | 
            +
                class Declare
         | 
| 7 | 
            +
                  def initialize(source, name, type, options)
         | 
| 8 | 
            +
                    @source = source
         | 
| 9 | 
            +
                    @name = name.to_sym
         | 
| 10 | 
            +
                    @type = type
         | 
| 11 | 
            +
                    @options = options
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def call
         | 
| 15 | 
            +
                    # Register new field metadata
         | 
| 16 | 
            +
                    @source.attributes = @source.attributes.merge(
         | 
| 17 | 
            +
                      @name => { type: @type }.merge(@options)
         | 
| 18 | 
            +
                    )
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    # Should be called before `define_attribute_methods` method because it
         | 
| 21 | 
            +
                    # defines an attribute getter itself
         | 
| 22 | 
            +
                    warn_about_method_overriding
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    # Dirty API
         | 
| 25 | 
            +
                    @source.define_attribute_method(@name)
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    # Generate getters and setters as well as other helper methods
         | 
| 28 | 
            +
                    generate_instance_methods
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    # If alias name specified - generate the same instance methods
         | 
| 31 | 
            +
                    if @options[:alias]
         | 
| 32 | 
            +
                      generate_instance_methods_for_alias
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  private
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  def warn_about_method_overriding
         | 
| 39 | 
            +
                    warn_if_method_exists(@name)
         | 
| 40 | 
            +
                    warn_if_method_exists("#{@name}=")
         | 
| 41 | 
            +
                    warn_if_method_exists("#{@name}?")
         | 
| 42 | 
            +
                    warn_if_method_exists("#{@name}_before_type_cast?")
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  def generate_instance_methods
         | 
| 46 | 
            +
                    # only local variable is visible in `module_eval` block
         | 
| 47 | 
            +
                    name = @name
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    @source.generated_methods.module_eval do
         | 
| 50 | 
            +
                      define_method(name) { read_attribute(name) }
         | 
| 51 | 
            +
                      define_method("#{name}?") do
         | 
| 52 | 
            +
                        value = read_attribute(name)
         | 
| 53 | 
            +
                        case value
         | 
| 54 | 
            +
                        when true        then true
         | 
| 55 | 
            +
                        when false, nil  then false
         | 
| 56 | 
            +
                        else
         | 
| 57 | 
            +
                          !value.nil?
         | 
| 58 | 
            +
                        end
         | 
| 59 | 
            +
                      end
         | 
| 60 | 
            +
                      define_method("#{name}=") { |value| write_attribute(name, value) }
         | 
| 61 | 
            +
                      define_method("#{name}_before_type_cast") { read_attribute_before_type_cast(name) }
         | 
| 62 | 
            +
                    end
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  def generate_instance_methods_for_alias
         | 
| 66 | 
            +
                    # only local variable is visible in `module_eval` block
         | 
| 67 | 
            +
                    name = @name
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                    alias_name = @options[:alias].to_sym
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                    @source.generated_methods.module_eval do
         | 
| 72 | 
            +
                      alias_method alias_name, name
         | 
| 73 | 
            +
                      alias_method "#{alias_name}=", "#{name}="
         | 
| 74 | 
            +
                      alias_method "#{alias_name}?", "#{name}?"
         | 
| 75 | 
            +
                      alias_method "#{alias_name}_before_type_cast", "#{name}_before_type_cast"
         | 
| 76 | 
            +
                    end
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  def warn_if_method_exists(method)
         | 
| 80 | 
            +
                    if @source.instance_methods.include?(method.to_sym)
         | 
| 81 | 
            +
                      Dynamoid.logger.warn("Method #{method} generated for the field #{@name} overrides already existing method")
         | 
| 82 | 
            +
                    end
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
            end
         | 
    
        data/lib/dynamoid/indexes.rb
    CHANGED
    
    | @@ -154,6 +154,16 @@ module Dynamoid | |
| 154 154 | 
             
                    index
         | 
| 155 155 | 
             
                  end
         | 
| 156 156 |  | 
| 157 | 
            +
                  # Returns an index by its name
         | 
| 158 | 
            +
                  #
         | 
| 159 | 
            +
                  # @param name [string, symbol] the name of the index to lookup
         | 
| 160 | 
            +
                  # @return [Dynamoid::Indexes::Index, nil] index object or nil if it isn't found
         | 
| 161 | 
            +
                  def find_index_by_name(name)
         | 
| 162 | 
            +
                    string_name = name.to_s
         | 
| 163 | 
            +
                    indexes.each_value.detect{ |i| i.name.to_s == string_name }
         | 
| 164 | 
            +
                  end
         | 
| 165 | 
            +
             | 
| 166 | 
            +
             | 
| 157 167 | 
             
                  # Returns true iff the provided hash[,range] key combo is a local
         | 
| 158 168 | 
             
                  # secondary index.
         | 
| 159 169 | 
             
                  #
         | 
    
        data/lib/dynamoid/loadable.rb
    CHANGED
    
    | @@ -10,7 +10,7 @@ module Dynamoid | |
| 10 10 | 
             
                  end
         | 
| 11 11 | 
             
                end
         | 
| 12 12 |  | 
| 13 | 
            -
                # Reload an object from the database -- if you suspect the object has changed in the  | 
| 13 | 
            +
                # Reload an object from the database -- if you suspect the object has changed in the data store and you need those
         | 
| 14 14 | 
             
                # changes to be reflected immediately, you would call this method. This is a consistent read.
         | 
| 15 15 | 
             
                #
         | 
| 16 16 | 
             
                # @return [Dynamoid::Document] the document this method was called on
         | 
| @@ -23,7 +23,7 @@ module Dynamoid | |
| 23 23 | 
             
                    options[:range_key] = range_value
         | 
| 24 24 | 
             
                  end
         | 
| 25 25 |  | 
| 26 | 
            -
                  self.attributes = self.class.find(hash_key, options).attributes
         | 
| 26 | 
            +
                  self.attributes = self.class.find(hash_key, **options).attributes
         | 
| 27 27 | 
             
                  @associations.values.each(&:reset)
         | 
| 28 28 | 
             
                  self
         | 
| 29 29 | 
             
                end
         | 
    
        data/lib/dynamoid/persistence.rb
    CHANGED
    
    | @@ -8,11 +8,12 @@ require 'dynamoid/persistence/import' | |
| 8 8 | 
             
            require 'dynamoid/persistence/update_fields'
         | 
| 9 9 | 
             
            require 'dynamoid/persistence/upsert'
         | 
| 10 10 | 
             
            require 'dynamoid/persistence/save'
         | 
| 11 | 
            +
            require 'dynamoid/persistence/update_validations'
         | 
| 11 12 |  | 
| 12 13 | 
             
            # encoding: utf-8
         | 
| 13 14 | 
             
            module Dynamoid
         | 
| 14 | 
            -
              # | 
| 15 | 
            -
              # | 
| 15 | 
            +
              # Persistence is responsible for dumping objects to and marshalling objects from the data store. It tries to reserialize
         | 
| 16 | 
            +
              # values to be of the same type as when they were passed in, based on the fields in the class.
         | 
| 16 17 | 
             
              module Persistence
         | 
| 17 18 | 
             
                extend ActiveSupport::Concern
         | 
| 18 19 |  | 
| @@ -111,7 +112,7 @@ module Dynamoid | |
| 111 112 |  | 
| 112 113 | 
             
                    if created_successfuly && self.options[:expires]
         | 
| 113 114 | 
             
                      attribute = self.options[:expires][:field]
         | 
| 114 | 
            -
                      Dynamoid.adapter.update_time_to_live(table_name | 
| 115 | 
            +
                      Dynamoid.adapter.update_time_to_live(table_name, attribute)
         | 
| 115 116 | 
             
                    end
         | 
| 116 117 | 
             
                  end
         | 
| 117 118 |  | 
| @@ -276,6 +277,9 @@ module Dynamoid | |
| 276 277 | 
             
                  # +update_fields+ uses the +UpdateItem+ operation so it saves changes and
         | 
| 277 278 | 
             
                  # loads an updated document back with one HTTP request.
         | 
| 278 279 | 
             
                  #
         | 
| 280 | 
            +
                  # Raises a +Dynamoid::Errors::UnknownAttribute+ exception if any of the
         | 
| 281 | 
            +
                  # attributes is not on the model
         | 
| 282 | 
            +
                  #
         | 
| 279 283 | 
             
                  # @param hash_key_value [Scalar value] hash key
         | 
| 280 284 | 
             
                  # @param range_key_value [Scalar value] range key (optional)
         | 
| 281 285 | 
             
                  # @param attrs [Hash]
         | 
| @@ -324,6 +328,9 @@ module Dynamoid | |
| 324 328 | 
             
                  # +upsert+ uses the +UpdateItem+ operation so it saves changes and loads
         | 
| 325 329 | 
             
                  # an updated document back with one HTTP request.
         | 
| 326 330 | 
             
                  #
         | 
| 331 | 
            +
                  # Raises a +Dynamoid::Errors::UnknownAttribute+ exception if any of the
         | 
| 332 | 
            +
                  # attributes is not on the model
         | 
| 333 | 
            +
                  #
         | 
| 327 334 | 
             
                  # @param hash_key_value [Scalar value] hash key
         | 
| 328 335 | 
             
                  # @param range_key_value [Scalar value] range key (optional)
         | 
| 329 336 | 
             
                  # @param attrs [Hash]
         | 
| @@ -491,6 +498,9 @@ module Dynamoid | |
| 491 498 | 
             
                #
         | 
| 492 499 | 
             
                #   user.update_attributes(age: 27, last_name: 'Tylor')
         | 
| 493 500 | 
             
                #
         | 
| 501 | 
            +
                # Raises a +Dynamoid::Errors::UnknownAttribute+ exception if any of the
         | 
| 502 | 
            +
                # attributes is not on the model
         | 
| 503 | 
            +
                #
         | 
| 494 504 | 
             
                # @param attributes [Hash] a hash of attributes to update
         | 
| 495 505 | 
             
                # @return [true|false] Whether updating successful or not
         | 
| 496 506 | 
             
                # @since 0.2.0
         | 
| @@ -507,6 +517,9 @@ module Dynamoid | |
| 507 517 | 
             
                # Raises a +Dynamoid::Errors::DocumentNotValid+ exception if some vaidation
         | 
| 508 518 | 
             
                # fails.
         | 
| 509 519 | 
             
                #
         | 
| 520 | 
            +
                # Raises a +Dynamoid::Errors::UnknownAttribute+ exception if any of the
         | 
| 521 | 
            +
                # attributes is not on the model
         | 
| 522 | 
            +
                #
         | 
| 510 523 | 
             
                # @param attributes [Hash] a hash of attributes to update
         | 
| 511 524 | 
             
                def update_attributes!(attributes)
         | 
| 512 525 | 
             
                  attributes.each { |attribute, value| write_attribute(attribute, value) }
         | 
| @@ -519,6 +532,9 @@ module Dynamoid | |
| 519 532 | 
             
                #
         | 
| 520 533 | 
             
                #   user.update_attribute(:last_name, 'Tylor')
         | 
| 521 534 | 
             
                #
         | 
| 535 | 
            +
                # Raises a +Dynamoid::Errors::UnknownAttribute+ exception if any of the
         | 
| 536 | 
            +
                # attributes is not on the model
         | 
| 537 | 
            +
                #
         | 
| 522 538 | 
             
                # @param attribute [Symbol] attribute name to update
         | 
| 523 539 | 
             
                # @param value [Object] the value to assign it
         | 
| 524 540 | 
             
                # @return [Dynamoid::Document] self
         | 
| @@ -786,6 +802,10 @@ module Dynamoid | |
| 786 802 | 
             
                  @destroyed = true
         | 
| 787 803 |  | 
| 788 804 | 
             
                  Dynamoid.adapter.delete(self.class.table_name, hash_key, options)
         | 
| 805 | 
            +
             | 
| 806 | 
            +
                  self.class.associations.each do |name, options|
         | 
| 807 | 
            +
                    send(name).disassociate_source
         | 
| 808 | 
            +
                  end
         | 
| 789 809 | 
             
                rescue Dynamoid::Errors::ConditionalCheckFailedException
         | 
| 790 810 | 
             
                  raise Dynamoid::Errors::StaleObjectError.new(self, 'delete')
         | 
| 791 811 | 
             
                end
         | 
| @@ -4,8 +4,8 @@ module Dynamoid | |
| 4 4 | 
             
              module Persistence
         | 
| 5 5 | 
             
                # @private
         | 
| 6 6 | 
             
                class UpdateFields
         | 
| 7 | 
            -
                  def self.call(*args)
         | 
| 8 | 
            -
                    new(*args).call
         | 
| 7 | 
            +
                  def self.call(*args, **options)
         | 
| 8 | 
            +
                    new(*args, **options).call
         | 
| 9 9 | 
             
                  end
         | 
| 10 10 |  | 
| 11 11 | 
             
                  def initialize(model_class, partition_key:, sort_key:, attributes:, conditions:)
         | 
| @@ -17,6 +17,8 @@ module Dynamoid | |
| 17 17 | 
             
                  end
         | 
| 18 18 |  | 
| 19 19 | 
             
                  def call
         | 
| 20 | 
            +
                    UpdateValidations.validate_attributes_exist(@model_class, @attributes)
         | 
| 21 | 
            +
             | 
| 20 22 | 
             
                    if Dynamoid::Config.timestamps
         | 
| 21 23 | 
             
                      @attributes[:updated_at] ||= DateTime.now.in_time_zone(Time.zone)
         | 
| 22 24 | 
             
                    end
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Dynamoid
         | 
| 4 | 
            +
              module Persistence
         | 
| 5 | 
            +
                # @private
         | 
| 6 | 
            +
                module UpdateValidations
         | 
| 7 | 
            +
                  def self.validate_attributes_exist(model_class, attributes)
         | 
| 8 | 
            +
                    model_attributes = model_class.attributes.keys
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    attributes.each do |attr_name, _|
         | 
| 11 | 
            +
                      unless model_attributes.include?(attr_name)
         | 
| 12 | 
            +
                        raise Dynamoid::Errors::UnknownAttribute.new("Attribute #{attr_name} does not exist in #{model_class}")
         | 
| 13 | 
            +
                      end
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
            end
         | 
| @@ -4,8 +4,8 @@ module Dynamoid | |
| 4 4 | 
             
              module Persistence
         | 
| 5 5 | 
             
                # @private
         | 
| 6 6 | 
             
                class Upsert
         | 
| 7 | 
            -
                  def self.call(*args)
         | 
| 8 | 
            -
                    new(*args).call
         | 
| 7 | 
            +
                  def self.call(*args, **options)
         | 
| 8 | 
            +
                    new(*args, **options).call
         | 
| 9 9 | 
             
                  end
         | 
| 10 10 |  | 
| 11 11 | 
             
                  def initialize(model_class, partition_key:, sort_key:, attributes:, conditions:)
         | 
| @@ -17,6 +17,8 @@ module Dynamoid | |
| 17 17 | 
             
                  end
         | 
| 18 18 |  | 
| 19 19 | 
             
                  def call
         | 
| 20 | 
            +
                    UpdateValidations.validate_attributes_exist(@model_class, @attributes)
         | 
| 21 | 
            +
             | 
| 20 22 | 
             
                    if Dynamoid::Config.timestamps
         | 
| 21 23 | 
             
                      @attributes[:updated_at] ||= DateTime.now.in_time_zone(Time.zone)
         | 
| 22 24 | 
             
                    end
         | 
    
        data/lib/dynamoid/validations.rb
    CHANGED
    
    | @@ -50,7 +50,10 @@ module Dynamoid | |
| 50 50 | 
             
                  class PresenceValidator < ActiveModel::EachValidator
         | 
| 51 51 | 
             
                    # Validate the record for the record and value.
         | 
| 52 52 | 
             
                    def validate_each(record, attr_name, value)
         | 
| 53 | 
            -
                       | 
| 53 | 
            +
                      # Use keyword argument `options` because it was a Hash in Rails < 6.1
         | 
| 54 | 
            +
                      # and became a keyword argument in 6.1.  This way it works in both
         | 
| 55 | 
            +
                      # cases.
         | 
| 56 | 
            +
                      record.errors.add(attr_name, :blank, **options) if not_present?(value)
         | 
| 54 57 | 
             
                    end
         | 
| 55 58 |  | 
| 56 59 | 
             
                    private
         | 
    
        data/lib/dynamoid/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: dynamoid
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 3. | 
| 4 | 
            +
              version: 3.7.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Josh Symonds
         | 
| @@ -18,10 +18,10 @@ authors: | |
| 18 18 | 
             
            - Brian Glusman
         | 
| 19 19 | 
             
            - Peter Boling
         | 
| 20 20 | 
             
            - Andrew Konchin
         | 
| 21 | 
            -
            autorequire: | 
| 21 | 
            +
            autorequire:
         | 
| 22 22 | 
             
            bindir: bin
         | 
| 23 23 | 
             
            cert_chain: []
         | 
| 24 | 
            -
            date:  | 
| 24 | 
            +
            date: 2021-02-02 00:00:00.000000000 Z
         | 
| 25 25 | 
             
            dependencies:
         | 
| 26 26 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 27 27 | 
             
              name: activemodel
         | 
| @@ -244,6 +244,7 @@ files: | |
| 244 244 | 
             
            - lib/dynamoid/dynamodb_time_zone.rb
         | 
| 245 245 | 
             
            - lib/dynamoid/errors.rb
         | 
| 246 246 | 
             
            - lib/dynamoid/fields.rb
         | 
| 247 | 
            +
            - lib/dynamoid/fields/declare.rb
         | 
| 247 248 | 
             
            - lib/dynamoid/finders.rb
         | 
| 248 249 | 
             
            - lib/dynamoid/identity_map.rb
         | 
| 249 250 | 
             
            - lib/dynamoid/indexes.rb
         | 
| @@ -254,6 +255,7 @@ files: | |
| 254 255 | 
             
            - lib/dynamoid/persistence/import.rb
         | 
| 255 256 | 
             
            - lib/dynamoid/persistence/save.rb
         | 
| 256 257 | 
             
            - lib/dynamoid/persistence/update_fields.rb
         | 
| 258 | 
            +
            - lib/dynamoid/persistence/update_validations.rb
         | 
| 257 259 | 
             
            - lib/dynamoid/persistence/upsert.rb
         | 
| 258 260 | 
             
            - lib/dynamoid/primary_key_type_mapping.rb
         | 
| 259 261 | 
             
            - lib/dynamoid/railtie.rb
         | 
| @@ -269,10 +271,10 @@ licenses: | |
| 269 271 | 
             
            - MIT
         | 
| 270 272 | 
             
            metadata:
         | 
| 271 273 | 
             
              bug_tracker_uri: https://github.com/Dynamoid/dynamoid/issues
         | 
| 272 | 
            -
              changelog_uri: https://github.com/Dynamoid/dynamoid/tree/v3. | 
| 273 | 
            -
              source_code_uri: https://github.com/Dynamoid/dynamoid/tree/v3. | 
| 274 | 
            -
              documentation_uri: https://rubydoc.info/gems/dynamoid/3. | 
| 275 | 
            -
            post_install_message: | 
| 274 | 
            +
              changelog_uri: https://github.com/Dynamoid/dynamoid/tree/v3.7.0/CHANGELOG.md
         | 
| 275 | 
            +
              source_code_uri: https://github.com/Dynamoid/dynamoid/tree/v3.7.0
         | 
| 276 | 
            +
              documentation_uri: https://rubydoc.info/gems/dynamoid/3.7.0
         | 
| 277 | 
            +
            post_install_message:
         | 
| 276 278 | 
             
            rdoc_options: []
         | 
| 277 279 | 
             
            require_paths:
         | 
| 278 280 | 
             
            - lib
         | 
| @@ -287,8 +289,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 287 289 | 
             
                - !ruby/object:Gem::Version
         | 
| 288 290 | 
             
                  version: '0'
         | 
| 289 291 | 
             
            requirements: []
         | 
| 290 | 
            -
            rubygems_version: 3. | 
| 291 | 
            -
            signing_key: | 
| 292 | 
            +
            rubygems_version: 3.2.0
         | 
| 293 | 
            +
            signing_key:
         | 
| 292 294 | 
             
            specification_version: 4
         | 
| 293 295 | 
             
            summary: Dynamoid is an ORM for Amazon's DynamoDB
         | 
| 294 296 | 
             
            test_files: []
         |