dynamoid 0.5.0 → 0.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.
- data/Dynamoid.gemspec +3 -3
- data/README.markdown +72 -7
- data/VERSION +1 -1
- data/lib/dynamoid/adapter.rb +115 -23
- data/lib/dynamoid/adapter/aws_sdk.rb +50 -7
- data/lib/dynamoid/config.rb +1 -1
- data/lib/dynamoid/criteria.rb +1 -1
- data/lib/dynamoid/criteria/chain.rb +76 -4
- data/lib/dynamoid/document.rb +6 -1
- data/lib/dynamoid/fields.rb +17 -1
- data/lib/dynamoid/persistence.rb +22 -6
- data/spec/app/models/address.rb +1 -0
- data/spec/dynamoid/adapter/aws_sdk_spec.rb +198 -1
- data/spec/dynamoid/adapter_spec.rb +55 -2
- data/spec/dynamoid/criteria/chain_spec.rb +23 -0
- data/spec/dynamoid/document_spec.rb +3 -3
- data/spec/dynamoid/fields_spec.rb +4 -4
- data/spec/dynamoid/persistence_spec.rb +46 -0
- metadata +4 -4
    
        data/Dynamoid.gemspec
    CHANGED
    
    | @@ -5,11 +5,11 @@ | |
| 5 5 |  | 
| 6 6 | 
             
            Gem::Specification.new do |s|
         | 
| 7 7 | 
             
              s.name = "dynamoid"
         | 
| 8 | 
            -
              s.version = "0. | 
| 8 | 
            +
              s.version = "0.6.0"
         | 
| 9 9 |  | 
| 10 10 | 
             
              s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
         | 
| 11 11 | 
             
              s.authors = ["Josh Symonds"]
         | 
| 12 | 
            -
              s.date = "2012- | 
| 12 | 
            +
              s.date = "2012-12-19"
         | 
| 13 13 | 
             
              s.description = "Dynamoid is an ORM for Amazon's DynamoDB that supports offline development, associations, querying, and everything else you'd expect from an ActiveRecord-style replacement."
         | 
| 14 14 | 
             
              s.email = "josh@joshsymonds.com"
         | 
| 15 15 | 
             
              s.extra_rdoc_files = [
         | 
| @@ -139,7 +139,7 @@ Gem::Specification.new do |s| | |
| 139 139 | 
             
              s.homepage = "http://github.com/Veraticus/Dynamoid"
         | 
| 140 140 | 
             
              s.licenses = ["MIT"]
         | 
| 141 141 | 
             
              s.require_paths = ["lib"]
         | 
| 142 | 
            -
              s.rubygems_version = "1.8. | 
| 142 | 
            +
              s.rubygems_version = "1.8.24"
         | 
| 143 143 | 
             
              s.summary = "Dynamoid is an ORM for Amazon's DynamoDB"
         | 
| 144 144 |  | 
| 145 145 | 
             
              if s.respond_to? :specification_version then
         | 
    
        data/README.markdown
    CHANGED
    
    | @@ -17,15 +17,72 @@ Installing Dynamoid is pretty simple. First include the Gem in your Gemfile: | |
| 17 17 | 
             
            ```ruby
         | 
| 18 18 | 
             
            gem 'dynamoid'
         | 
| 19 19 | 
             
            ```
         | 
| 20 | 
            +
            ## Prerequisities
         | 
| 20 21 |  | 
| 21 | 
            -
             | 
| 22 | 
            +
            Dynamoid depends on  the aws-sdk, and this is tested on the current version of aws-sdk (1.6.9), rails 3.2.8.
         | 
| 23 | 
            +
            Hence the configuration as needed for aws to work will be dealt with by aws setup. 
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            Here are the steps to setup aws-sdk.
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            ```ruby
         | 
| 28 | 
            +
            gem 'aws-sdk'
         | 
| 29 | 
            +
            ```
         | 
| 30 | 
            +
            (or) include the aws-sdk in your Gemfile.
         | 
| 31 | 
            +
             | 
| 32 | 
            +
             | 
| 33 | 
            +
            [Refer this link for aws setup](https://github.com/amazonwebservices/aws-sdk-for-ruby)
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            1. Just like the config/database.yml this file requires an entry for each environment, create config/aws.yml as follows:
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            Fill in your AWS Access Key ID and Secret Access Key
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            ```ruby
         | 
| 40 | 
            +
             | 
| 41 | 
            +
             | 
| 42 | 
            +
              development:
         | 
| 43 | 
            +
                access_key_id: REPLACE_WITH_ACCESS_KEY_ID
         | 
| 44 | 
            +
                secret_access_key: REPLACE_WITH_SECRET_ACCESS_KEY
         | 
| 45 | 
            +
                dynamodb_end_point:  dynamodb.ap-southeast-1.amazonaws.com
         | 
| 46 | 
            +
             | 
| 47 | 
            +
              test:
         | 
| 48 | 
            +
                <<: *development
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              production:
         | 
| 51 | 
            +
                <<: *development
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            ```
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            (or)
         | 
| 56 | 
            +
             | 
| 57 | 
            +
             | 
| 58 | 
            +
            2. Create config/initializers/aws.rb as follows:
         | 
| 59 | 
            +
             | 
| 60 | 
            +
            ```ruby
         | 
| 61 | 
            +
             | 
| 62 | 
            +
            #Additionally include any of the dynamodb paramters as needed.
         | 
| 63 | 
            +
            #(eg: if you would like to change the dynamodb endpoint, then add the parameter in 
         | 
| 64 | 
            +
            # in the file  aws.yml or aws.rb 
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            dynamo_db_endpoint : dynamodb.ap-southeast-1.amazonaws.com)
         | 
| 67 | 
            +
             | 
| 68 | 
            +
             AWS.config({
         | 
| 69 | 
            +
                :access_key_id => 'REPLACE_WITH_ACCESS_KEY_ID',
         | 
| 70 | 
            +
                :secret_access_key => 'REPLACE_WITH_SECRET_ACCESS_KEY',
         | 
| 71 | 
            +
                :dynamodb_end_point => dynamodb.ap-southeast-1.amazonaws.com
         | 
| 72 | 
            +
              })
         | 
| 73 | 
            +
             | 
| 74 | 
            +
             | 
| 75 | 
            +
            ```
         | 
| 76 | 
            +
             | 
| 77 | 
            +
            Refer code in Module: AWS, and from the link below for the other configuration options supported for dynamodb.
         | 
| 78 | 
            +
             | 
| 79 | 
            +
            [Module AWS](http://docs.amazonwebservices.com/AWSRubySDK/latest/frames.html#!http%3A//docs.amazonwebservices.com/AWSRubySDK/latest/AWS.html)
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            Then you need to initialize Dynamoid config to get it going. Put code similar to this somewhere (a Rails initializer would be a great place for this if you're using Rails):
         | 
| 22 82 |  | 
| 23 83 | 
             
            ```ruby
         | 
| 24 84 | 
             
              Dynamoid.configure do |config|
         | 
| 25 | 
            -
                 | 
| 26 | 
            -
                # config.access_key = 'access_key' # If connecting to DynamoDB, your access key is required.
         | 
| 27 | 
            -
                # config.secret_key = 'secret_key' # So is your secret key.
         | 
| 28 | 
            -
                # config.endpoint = 'dynamodb.us-east-1.amazonaws.com' # Set the regional endpoint for DynamoDB.
         | 
| 85 | 
            +
                config.adapter = 'aws_sdk' # This adapter establishes a connection to the DynamoDB servers using Amazon's own AWS gem.
         | 
| 29 86 | 
             
                config.namespace = "dynamoid_app_development" # To namespace tables created by Dynamoid from other tables you might have.
         | 
| 30 87 | 
             
                config.warn_on_scan = true # Output a warning to the logger when you perform a scan rather than a query on a table.
         | 
| 31 88 | 
             
                config.partitioning = true # Spread writes randomly across the database. See "partitioning" below for more.
         | 
| @@ -252,14 +309,22 @@ Dynamoid borrows code, structure, and even its name very liberally from the trul | |
| 252 309 |  | 
| 253 310 | 
             
            Also, without contributors the project wouldn't be nearly as awesome. So many thanks to:
         | 
| 254 311 |  | 
| 312 | 
            +
            * [Logan Bowers](https://github.com/loganb)
         | 
| 313 | 
            +
            * [Lane LaRue](https://github.com/luxx)
         | 
| 314 | 
            +
            * [Craig Heneveld](https://github.com/cheneveld)
         | 
| 255 315 | 
             
            * [Anantha Kumaran](https://github.com/ananthakumaran)
         | 
| 256 316 | 
             
            * [Jason Dew](https://github.com/jasondew)
         | 
| 257 317 |  | 
| 258 318 | 
             
            ## Running the tests
         | 
| 259 319 |  | 
| 260 | 
            -
            Running the tests is fairly simple. In one window, run `fake_dynamo`, and in the other, use `rake`.
         | 
| 320 | 
            +
            Running the tests is fairly simple. In one window, run `fake_dynamo --port 4567`, and in the other, use `rake`.
         | 
| 261 321 |  | 
| 262 322 | 
             
            ## Copyright
         | 
| 263 323 |  | 
| 264 | 
            -
            Copyright (c) 2012 Josh Symonds. | 
| 324 | 
            +
            Copyright (c) 2012 Josh Symonds.
         | 
| 325 | 
            +
             | 
| 326 | 
            +
            Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
         | 
| 327 | 
            +
             | 
| 328 | 
            +
            The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
         | 
| 265 329 |  | 
| 330 | 
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
    
        data/VERSION
    CHANGED
    
    | @@ -1 +1 @@ | |
| 1 | 
            -
            0. | 
| 1 | 
            +
            0.6.0
         | 
    
        data/lib/dynamoid/adapter.rb
    CHANGED
    
    | @@ -45,16 +45,17 @@ module Dynamoid | |
| 45 45 | 
             
                #
         | 
| 46 46 | 
             
                # @param [String] table the name of the table to write the object to
         | 
| 47 47 | 
             
                # @param [Object] object the object itself
         | 
| 48 | 
            +
                # @param [Hash] options Options that are passed to the put_item call
         | 
| 48 49 | 
             
                #
         | 
| 49 50 | 
             
                # @return [Object] the persisted object
         | 
| 50 51 | 
             
                #
         | 
| 51 52 | 
             
                # @since 0.2.0
         | 
| 52 | 
            -
                def write(table, object)
         | 
| 53 | 
            +
                def write(table, object, options = nil)
         | 
| 53 54 | 
             
                  if Dynamoid::Config.partitioning? && object[:id]
         | 
| 54 55 | 
             
                    object[:id] = "#{object[:id]}.#{Random.rand(Dynamoid::Config.partition_size)}"
         | 
| 55 56 | 
             
                    object[:updated_at] = Time.now.to_f
         | 
| 56 57 | 
             
                  end
         | 
| 57 | 
            -
                  put_item(table, object)
         | 
| 58 | 
            +
                  put_item(table, object, options)
         | 
| 58 59 | 
             
                end
         | 
| 59 60 |  | 
| 60 61 | 
             
                # Read one or many keys from the selected table. This method intelligently calls batch_get or get on the underlying adapter depending on
         | 
| @@ -73,7 +74,7 @@ module Dynamoid | |
| 73 74 | 
             
                    ids = ids.collect{|id| range_key ? [id, range_key] : id}
         | 
| 74 75 | 
             
                    if Dynamoid::Config.partitioning?
         | 
| 75 76 | 
             
                      results = batch_get_item(table => id_with_partitions(ids))
         | 
| 76 | 
            -
                      {table => result_for_partition(results[table])}
         | 
| 77 | 
            +
                      {table => result_for_partition(results[table],table)}
         | 
| 77 78 | 
             
                    else
         | 
| 78 79 | 
             
                      batch_get_item(table => ids)
         | 
| 79 80 | 
             
                    end
         | 
| @@ -81,7 +82,7 @@ module Dynamoid | |
| 81 82 | 
             
                    if Dynamoid::Config.partitioning?
         | 
| 82 83 | 
             
                      ids = range_key ? [[ids, range_key]] : ids
         | 
| 83 84 | 
             
                      results = batch_get_item(table => id_with_partitions(ids))
         | 
| 84 | 
            -
                      result_for_partition(results[table]).first
         | 
| 85 | 
            +
                      result_for_partition(results[table],table).first
         | 
| 85 86 | 
             
                    else
         | 
| 86 87 | 
             
                      get_item(table, ids, options)
         | 
| 87 88 | 
             
                    end
         | 
| @@ -91,15 +92,31 @@ module Dynamoid | |
| 91 92 | 
             
                # Delete an item from a table. If partitioning is turned on, deletes all partitioned keys as well.
         | 
| 92 93 | 
             
                #
         | 
| 93 94 | 
             
                # @param [String] table the name of the table to write the object to
         | 
| 94 | 
            -
                # @param [ | 
| 95 | 
            -
                # @param [ | 
| 95 | 
            +
                # @param [Array] ids to delete, can also be a string of just one id
         | 
| 96 | 
            +
                # @param [Array] range_key of the record to delete, can also be a string of just one range_key
         | 
| 96 97 | 
             
                #
         | 
| 97 | 
            -
                 | 
| 98 | 
            -
             | 
| 99 | 
            -
                  if  | 
| 100 | 
            -
                     | 
| 98 | 
            +
                def delete(table, ids, options = {})
         | 
| 99 | 
            +
                  range_key = options[:range_key] #array of range keys that matches the ids passed in
         | 
| 100 | 
            +
                  if ids.respond_to?(:each)
         | 
| 101 | 
            +
                    if range_key.respond_to?(:each)
         | 
| 102 | 
            +
                      #turn ids into array of arrays each element being hash_key, range_key
         | 
| 103 | 
            +
                      ids = ids.each_with_index.map{|id,i| [id,range_key[i]]}
         | 
| 104 | 
            +
                    else
         | 
| 105 | 
            +
                      ids = range_key ? [[ids, range_key]] : ids
         | 
| 106 | 
            +
                    end
         | 
| 107 | 
            +
                    
         | 
| 108 | 
            +
                    if Dynamoid::Config.partitioning?
         | 
| 109 | 
            +
                      batch_delete_item(table => id_with_partitions(ids))
         | 
| 110 | 
            +
                    else
         | 
| 111 | 
            +
                      batch_delete_item(table => ids)
         | 
| 112 | 
            +
                    end
         | 
| 101 113 | 
             
                  else
         | 
| 102 | 
            -
                     | 
| 114 | 
            +
                    if Dynamoid::Config.partitioning?
         | 
| 115 | 
            +
                      ids = range_key ? [[ids, range_key]] : ids
         | 
| 116 | 
            +
                      batch_delete_item(table => id_with_partitions(ids))
         | 
| 117 | 
            +
                    else
         | 
| 118 | 
            +
                      delete_item(table, ids, options)
         | 
| 119 | 
            +
                    end
         | 
| 103 120 | 
             
                  end
         | 
| 104 121 | 
             
                end
         | 
| 105 122 |  | 
| @@ -112,7 +129,7 @@ module Dynamoid | |
| 112 129 | 
             
                def scan(table, query, opts = {})
         | 
| 113 130 | 
             
                  if Dynamoid::Config.partitioning?
         | 
| 114 131 | 
             
                    results = benchmark('Scan', table, query) {adapter.scan(table, query, opts)}
         | 
| 115 | 
            -
                    result_for_partition(results)
         | 
| 132 | 
            +
                    result_for_partition(results,table)
         | 
| 116 133 | 
             
                  else
         | 
| 117 134 | 
             
                    benchmark('Scan', table, query) {adapter.scan(table, query, opts)}
         | 
| 118 135 | 
             
                  end
         | 
| @@ -141,24 +158,59 @@ module Dynamoid | |
| 141 158 | 
             
                def id_with_partitions(ids)
         | 
| 142 159 | 
             
                  Array(ids).collect {|id| (0...Dynamoid::Config.partition_size).collect{|n| id.is_a?(Array) ? ["#{id.first}.#{n}", id.last] : "#{id}.#{n}"}}.flatten(1)
         | 
| 143 160 | 
             
                end
         | 
| 161 | 
            +
                
         | 
| 162 | 
            +
                #Get original id (hash_key) and partiton number from a hash_key
         | 
| 163 | 
            +
                #
         | 
| 164 | 
            +
                # @param [String] id the id or hash_key of a record, ex. xxxxx.13
         | 
| 165 | 
            +
                #
         | 
| 166 | 
            +
                # @return [String,String] original_id and the partition number, ex original_id = xxxxx partition = 13
         | 
| 167 | 
            +
                def get_original_id_and_partition id
         | 
| 168 | 
            +
                  partition = id.split('.').last
         | 
| 169 | 
            +
                  id = id.split(".#{partition}").first
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                  return id, partition
         | 
| 172 | 
            +
                end
         | 
| 144 173 |  | 
| 145 | 
            -
                # Takes an array of results that are partitioned, find the most recently updated  | 
| 174 | 
            +
                # Takes an array of query results that are partitioned, find the most recently updated ones that share an id and range_key, and return only the most recently updated. Compares each result by
         | 
| 146 175 | 
             
                # their id and updated_at attributes; if the updated_at is the greatest, then it must be the correct result.
         | 
| 147 176 | 
             
                #
         | 
| 148 177 | 
             
                # @param [Array] returned partitioned results from a query
         | 
| 178 | 
            +
                # @param [String] table_name the name of the table
         | 
| 149 179 | 
             
                #
         | 
| 150 180 | 
             
                # @since 0.2.0
         | 
| 151 | 
            -
                def result_for_partition(results)
         | 
| 152 | 
            -
                   | 
| 153 | 
            -
             | 
| 154 | 
            -
             | 
| 155 | 
            -
             | 
| 156 | 
            -
             | 
| 157 | 
            -
             | 
| 158 | 
            -
             | 
| 181 | 
            +
                def result_for_partition(results, table_name)
         | 
| 182 | 
            +
                  table = Dynamoid::Adapter::AwsSdk.get_table(table_name)
         | 
| 183 | 
            +
                  
         | 
| 184 | 
            +
                  if table.range_key     
         | 
| 185 | 
            +
                    range_key_name = table.range_key.name.to_sym
         | 
| 186 | 
            +
                    
         | 
| 187 | 
            +
                    final_hash = {}
         | 
| 188 | 
            +
                    
         | 
| 189 | 
            +
                    results.each do |record|
         | 
| 190 | 
            +
                      test_record = final_hash[record[range_key_name]]
         | 
| 191 | 
            +
                      
         | 
| 192 | 
            +
                      if test_record.nil? || ((record[range_key_name] == test_record[range_key_name]) && (record[:updated_at] > test_record[:updated_at]))
         | 
| 193 | 
            +
                        #get ride of our partition and put it in the array with the range key
         | 
| 194 | 
            +
                        record[:id], partition = get_original_id_and_partition  record[:id]
         | 
| 195 | 
            +
                        final_hash[record[range_key_name]] = record
         | 
| 159 196 | 
             
                      end
         | 
| 160 197 | 
             
                    end
         | 
| 161 | 
            -
             | 
| 198 | 
            +
              
         | 
| 199 | 
            +
                    return final_hash.values
         | 
| 200 | 
            +
                  else
         | 
| 201 | 
            +
                    {}.tap do |hash|
         | 
| 202 | 
            +
                      Array(results).each do |result|
         | 
| 203 | 
            +
                        next if result.nil?
         | 
| 204 | 
            +
                        #Need to find the value of id with out the . and partition number
         | 
| 205 | 
            +
                        id, partition = get_original_id_and_partition result[:id]
         | 
| 206 | 
            +
              
         | 
| 207 | 
            +
                        if !hash[id] || (result[:updated_at] > hash[id][:updated_at])
         | 
| 208 | 
            +
                          result[:id] = id
         | 
| 209 | 
            +
                          hash[id] = result
         | 
| 210 | 
            +
                        end
         | 
| 211 | 
            +
                      end
         | 
| 212 | 
            +
                    end.values
         | 
| 213 | 
            +
                  end
         | 
| 162 214 | 
             
                end
         | 
| 163 215 |  | 
| 164 216 | 
             
                # Delegate all methods that aren't defind here to the underlying adapter.
         | 
| @@ -168,7 +220,47 @@ module Dynamoid | |
| 168 220 | 
             
                  return benchmark(method, *args) {adapter.send(method, *args, &block)} if @adapter.respond_to?(method)
         | 
| 169 221 | 
             
                  super
         | 
| 170 222 | 
             
                end
         | 
| 223 | 
            +
                
         | 
| 224 | 
            +
                # Query the DynamoDB table. This employs DynamoDB's indexes so is generally faster than scanning, but is
         | 
| 225 | 
            +
                # only really useful for range queries, since it can only find by one hash key at once. Only provide
         | 
| 226 | 
            +
                # one range key to the hash. If paritioning is on, will run a query for every parition and join the results
         | 
| 227 | 
            +
                #
         | 
| 228 | 
            +
                # @param [String] table_name the name of the table
         | 
| 229 | 
            +
                # @param [Hash] opts the options to query the table with
         | 
| 230 | 
            +
                # @option opts [String] :hash_value the value of the hash key to find
         | 
| 231 | 
            +
                # @option opts [Range] :range_value find the range key within this range
         | 
| 232 | 
            +
                # @option opts [Number] :range_greater_than find range keys greater than this
         | 
| 233 | 
            +
                # @option opts [Number] :range_less_than find range keys less than this
         | 
| 234 | 
            +
                # @option opts [Number] :range_gte find range keys greater than or equal to this
         | 
| 235 | 
            +
                # @option opts [Number] :range_lte find range keys less than or equal to this
         | 
| 236 | 
            +
                #
         | 
| 237 | 
            +
                # @return [Array] an array of all matching items
         | 
| 238 | 
            +
                #
         | 
| 239 | 
            +
                def query(table_name, opts = {})
         | 
| 240 | 
            +
                  
         | 
| 241 | 
            +
                  unless Dynamoid::Config.partitioning?
         | 
| 242 | 
            +
                    #no paritioning? just pass to the standard query method
         | 
| 243 | 
            +
                    Dynamoid::Adapter::AwsSdk.query(table_name, opts)
         | 
| 244 | 
            +
                  else
         | 
| 245 | 
            +
                    #get all the hash_values that could be possible
         | 
| 246 | 
            +
                    ids = id_with_partitions(opts[:hash_value])
         | 
| 171 247 |  | 
| 172 | 
            -
             | 
| 248 | 
            +
                    #lets not overwrite with the original options
         | 
| 249 | 
            +
                    modified_options = opts.clone     
         | 
| 250 | 
            +
                    results = []
         | 
| 251 | 
            +
                    
         | 
| 252 | 
            +
                    #loop and query on each of the partition ids
         | 
| 253 | 
            +
                    ids.each do |id|
         | 
| 254 | 
            +
                      modified_options[:hash_value] = id
         | 
| 255 | 
            +
             | 
| 256 | 
            +
                      query_result = Dynamoid::Adapter::AwsSdk.query(table_name, modified_options)
         | 
| 257 | 
            +
                      query_result = [query_result] if !query_result.is_a?(Array)
         | 
| 173 258 |  | 
| 259 | 
            +
                      results = results + query_result unless query_result.nil? 
         | 
| 260 | 
            +
                    end 
         | 
| 261 | 
            +
                    
         | 
| 262 | 
            +
                    result_for_partition results, table_name
         | 
| 263 | 
            +
                  end
         | 
| 264 | 
            +
                end
         | 
| 265 | 
            +
              end
         | 
| 174 266 | 
             
            end
         | 
| @@ -15,10 +15,30 @@ module Dynamoid | |
| 15 15 | 
             
                  # Establish the connection to DynamoDB.
         | 
| 16 16 | 
             
                  #
         | 
| 17 17 | 
             
                  # @return [AWS::DynamoDB::Connection] the raw DynamoDB connection
         | 
| 18 | 
            -
                  #
         | 
| 18 | 
            +
                  # Call DynamoDB new, with no parameters. 
         | 
| 19 | 
            +
                  # Make sure the aws.yml file or aws.rb file, refer the link for more details. 
         | 
| 20 | 
            +
                  #https://github.com/amazonwebservices/aws-sdk-for-ruby
         | 
| 21 | 
            +
                  # 1. Create config/aws.yml as follows:
         | 
| 22 | 
            +
                  # Fill in your AWS Access Key ID and Secret Access Key
         | 
| 23 | 
            +
                  # http://aws.amazon.com/security-credentials
         | 
| 24 | 
            +
                  #access_key_id: REPLACE_WITH_ACCESS_KEY_ID
         | 
| 25 | 
            +
                  #secret_access_key: REPLACE_WITH_SECRET_ACCESS_KEY
         | 
| 26 | 
            +
                  #(or)
         | 
| 27 | 
            +
                  #2, Create config/initializers/aws.rb as follows:
         | 
| 28 | 
            +
                  # load the libraries
         | 
| 29 | 
            +
                  #require 'aws'
         | 
| 30 | 
            +
                  # log requests using the default rails logger
         | 
| 31 | 
            +
                  #AWS.config(:logger => Rails.logger)
         | 
| 32 | 
            +
                  # load credentials from a file
         | 
| 33 | 
            +
                  #config_path = File.expand_path(File.dirname(__FILE__)+"/../aws.yml")
         | 
| 34 | 
            +
                  #AWS.config(YAML.load(File.read(config_path)))
         | 
| 35 | 
            +
                  #Additionally include any of the dynamodb paramters as needed 
         | 
| 36 | 
            +
                  #(eg: if you would like to change the dynamodb endpoint, then add the parameter in 
         | 
| 37 | 
            +
                  # the following paramter in the file  aws.yml or aws.rb 
         | 
| 38 | 
            +
                  # dynamo_db_endpoint : dynamodb.ap-southeast-1.amazonaws.com)
         | 
| 19 39 | 
             
                  # @since 0.2.0
         | 
| 20 40 | 
             
                  def connect!
         | 
| 21 | 
            -
             | 
| 41 | 
            +
                  @@connection = AWS::DynamoDB.new
         | 
| 22 42 | 
             
                  end
         | 
| 23 43 |  | 
| 24 44 | 
             
                  # Return the established connection.
         | 
| @@ -54,6 +74,29 @@ module Dynamoid | |
| 54 74 | 
             
                    end
         | 
| 55 75 | 
             
                    hash
         | 
| 56 76 | 
             
                  end
         | 
| 77 | 
            +
                  
         | 
| 78 | 
            +
                  # Delete many items at once from DynamoDB. More efficient than delete each item individually.
         | 
| 79 | 
            +
                  #
         | 
| 80 | 
            +
                  # @example Delete IDs 1 and 2 from the table testtable
         | 
| 81 | 
            +
                  #   Dynamoid::Adapter::AwsSdk.batch_delete_item('table1' => ['1', '2'])
         | 
| 82 | 
            +
                  #or
         | 
| 83 | 
            +
                  #   Dynamoid::Adapter::AwsSdk.batch_delete_item('table1' => [['hk1', 'rk2'], ['hk1', 'rk2']]]))
         | 
| 84 | 
            +
                  #
         | 
| 85 | 
            +
                  # @param [Hash] options the hash of tables and IDs to delete
         | 
| 86 | 
            +
                  #
         | 
| 87 | 
            +
                  # @return nil
         | 
| 88 | 
            +
                  #
         | 
| 89 | 
            +
                  def batch_delete_item(options)
         | 
| 90 | 
            +
                    return nil if options.all?{|k, v| v.empty?}
         | 
| 91 | 
            +
                    options.each do |t, ids|
         | 
| 92 | 
            +
                      Array(ids).in_groups_of(25, false) do |group|
         | 
| 93 | 
            +
                        batch = AWS::DynamoDB::BatchWrite.new(:config => @@connection.config)
         | 
| 94 | 
            +
                        batch.delete(t,group)
         | 
| 95 | 
            +
                        batch.process!          
         | 
| 96 | 
            +
                      end
         | 
| 97 | 
            +
                    end
         | 
| 98 | 
            +
                    nil
         | 
| 99 | 
            +
                  end
         | 
| 57 100 |  | 
| 58 101 | 
             
                  # Create a table on DynamoDB. This usually takes a long time to complete.
         | 
| 59 102 | 
             
                  #
         | 
| @@ -111,9 +154,6 @@ module Dynamoid | |
| 111 154 | 
             
                  # @return [Hash] a hash representing the raw item in DynamoDB
         | 
| 112 155 | 
             
                  #
         | 
| 113 156 | 
             
                  # @since 0.2.0
         | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 116 | 
            -
             | 
| 117 157 | 
             
                  def get_item(table_name, key, options = {})
         | 
| 118 158 | 
             
                    range_key = options.delete(:range_key)
         | 
| 119 159 | 
             
                    table = get_table(table_name)
         | 
| @@ -150,9 +190,12 @@ module Dynamoid | |
| 150 190 | 
             
                  # @param [Object] object a hash or Dynamoid object to persist
         | 
| 151 191 | 
             
                  #
         | 
| 152 192 | 
             
                  # @since 0.2.0
         | 
| 153 | 
            -
                  def put_item(table_name, object)
         | 
| 193 | 
            +
                  def put_item(table_name, object, options = nil)
         | 
| 154 194 | 
             
                    table = get_table(table_name)
         | 
| 155 | 
            -
                    table.items.create( | 
| 195 | 
            +
                    table.items.create(
         | 
| 196 | 
            +
                      object.delete_if{|k, v| v.nil? || (v.respond_to?(:empty?) && v.empty?)},
         | 
| 197 | 
            +
                      options || {}
         | 
| 198 | 
            +
                    )
         | 
| 156 199 | 
             
                  end
         | 
| 157 200 |  | 
| 158 201 | 
             
                  # Query the DynamoDB table. This employs DynamoDB's indexes so is generally faster than scanning, but is
         | 
    
        data/lib/dynamoid/config.rb
    CHANGED
    
    | @@ -23,7 +23,7 @@ module Dynamoid | |
| 23 23 | 
             
                option :partition_size, :default => 200
         | 
| 24 24 | 
             
                option :endpoint, :default => 'dynamodb.us-east-1.amazonaws.com'
         | 
| 25 25 | 
             
                option :use_ssl, :default => true
         | 
| 26 | 
            -
                option :port, :default => ' | 
| 26 | 
            +
                option :port, :default => '443'
         | 
| 27 27 | 
             
                option :included_models, :default => []
         | 
| 28 28 | 
             
                option :identity_map, :default => false
         | 
| 29 29 |  | 
    
        data/lib/dynamoid/criteria.rb
    CHANGED
    
    | @@ -9,7 +9,7 @@ module Dynamoid | |
| 9 9 |  | 
| 10 10 | 
             
                module ClassMethods
         | 
| 11 11 |  | 
| 12 | 
            -
                  [:where, :all, :first, :each, :limit, :start].each do |meth|
         | 
| 12 | 
            +
                  [:where, :all, :first, :each, :limit, :start, :scan_index_forward].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,6 +16,7 @@ module Dynamoid #:nodoc: | |
| 16 16 | 
             
                    @query = {}
         | 
| 17 17 | 
             
                    @source = source
         | 
| 18 18 | 
             
                    @consistent_read = false
         | 
| 19 | 
            +
                    @scan_index_forward = true
         | 
| 19 20 | 
             
                  end
         | 
| 20 21 |  | 
| 21 22 | 
             
                  # The workhorse method of the criteria chain. Each key in the passed in hash will become another criteria that the
         | 
| @@ -45,6 +46,63 @@ module Dynamoid #:nodoc: | |
| 45 46 | 
             
                  def all
         | 
| 46 47 | 
             
                    records
         | 
| 47 48 | 
             
                  end
         | 
| 49 | 
            +
                  
         | 
| 50 | 
            +
                  # Destroys all the records matching the criteria.
         | 
| 51 | 
            +
                  #
         | 
| 52 | 
            +
                  def destroy_all
         | 
| 53 | 
            +
                    ids = []
         | 
| 54 | 
            +
                    
         | 
| 55 | 
            +
                    if range?
         | 
| 56 | 
            +
                      ranges = []
         | 
| 57 | 
            +
                      Dynamoid::Adapter.query(source.table_name, range_query).collect do |hash| 
         | 
| 58 | 
            +
                        ids << hash[source.hash_key.to_sym]
         | 
| 59 | 
            +
                        ranges << hash[source.range_key.to_sym]
         | 
| 60 | 
            +
                      end
         | 
| 61 | 
            +
                      
         | 
| 62 | 
            +
                      Dynamoid::Adapter.delete(source.table_name, ids,{:range_key => ranges})
         | 
| 63 | 
            +
                    elsif index
         | 
| 64 | 
            +
                      #TODO: test this throughly and find a way to delete all index table records for one source record
         | 
| 65 | 
            +
                      if index.range_key?
         | 
| 66 | 
            +
                        results = Dynamoid::Adapter.query(index.table_name, index_query.merge(consistent_opts))
         | 
| 67 | 
            +
                      else
         | 
| 68 | 
            +
                        results = Dynamoid::Adapter.read(index.table_name, index_query[:hash_value], consistent_opts)
         | 
| 69 | 
            +
                      end
         | 
| 70 | 
            +
                      
         | 
| 71 | 
            +
                      results.collect do |hash| 
         | 
| 72 | 
            +
                        ids << hash[source.hash_key.to_sym]
         | 
| 73 | 
            +
                        index_ranges << hash[source.range_key.to_sym]
         | 
| 74 | 
            +
                      end
         | 
| 75 | 
            +
                    
         | 
| 76 | 
            +
                      unless ids.nil? || ids.empty?
         | 
| 77 | 
            +
                        ids = ids.to_a
         | 
| 78 | 
            +
              
         | 
| 79 | 
            +
                        if @start
         | 
| 80 | 
            +
                          ids = ids.drop_while { |id| id != @start.hash_key }.drop(1)
         | 
| 81 | 
            +
                          index_ranges = index_ranges.drop_while { |range| range != @start.hash_key }.drop(1) unless index_ranges.nil?
         | 
| 82 | 
            +
                        end
         | 
| 83 | 
            +
              
         | 
| 84 | 
            +
                        if @limit           
         | 
| 85 | 
            +
                          ids = ids.take(@limit) 
         | 
| 86 | 
            +
                          index_ranges = index_ranges.take(@limit)
         | 
| 87 | 
            +
                        end
         | 
| 88 | 
            +
                        
         | 
| 89 | 
            +
                        Dynamoid::Adapter.delete(source.table_name, ids)
         | 
| 90 | 
            +
                        
         | 
| 91 | 
            +
                        if index.range_key?
         | 
| 92 | 
            +
                          Dynamoid::Adapter.delete(index.table_name, ids,{:range_key => index_ranges})
         | 
| 93 | 
            +
                        else
         | 
| 94 | 
            +
                          Dynamoid::Adapter.delete(index.table_name, ids)
         | 
| 95 | 
            +
                        end
         | 
| 96 | 
            +
                        
         | 
| 97 | 
            +
                      end
         | 
| 98 | 
            +
                    else
         | 
| 99 | 
            +
                      Dynamoid::Adapter.scan(source.table_name, query, scan_opts).collect do |hash| 
         | 
| 100 | 
            +
                        ids << hash[source.hash_key.to_sym]
         | 
| 101 | 
            +
                      end
         | 
| 102 | 
            +
                      
         | 
| 103 | 
            +
                      Dynamoid::Adapter.delete(source.table_name, ids)
         | 
| 104 | 
            +
                    end   
         | 
| 105 | 
            +
                  end
         | 
| 48 106 |  | 
| 49 107 | 
             
                  # Returns the first record matching the criteria.
         | 
| 50 108 | 
             
                  #
         | 
| @@ -63,6 +121,11 @@ module Dynamoid #:nodoc: | |
| 63 121 | 
             
                    self
         | 
| 64 122 | 
             
                  end
         | 
| 65 123 |  | 
| 124 | 
            +
                  def scan_index_forward(scan_index_forward)
         | 
| 125 | 
            +
                    @scan_index_forward = scan_index_forward
         | 
| 126 | 
            +
                    self
         | 
| 127 | 
            +
                  end
         | 
| 128 | 
            +
             | 
| 66 129 | 
             
                  # Allows you to use the results of a search as an enumerable over the results found.
         | 
| 67 130 | 
             
                  #
         | 
| 68 131 | 
             
                  # @since 0.2.0
         | 
| @@ -145,7 +208,7 @@ module Dynamoid #:nodoc: | |
| 145 208 | 
             
                      raise Dynamoid::Errors::InvalidQuery, 'Consistent read is not supported by SCAN operation'
         | 
| 146 209 | 
             
                    end
         | 
| 147 210 |  | 
| 148 | 
            -
                    Dynamoid::Adapter.scan(source.table_name, query,  | 
| 211 | 
            +
                    Dynamoid::Adapter.scan(source.table_name, query, scan_opts).collect {|hash| source.from_database(hash) }
         | 
| 149 212 | 
             
                  end
         | 
| 150 213 |  | 
| 151 214 | 
             
                  # Format the provided query so that it can be used to query results from DynamoDB.
         | 
| @@ -190,7 +253,7 @@ module Dynamoid #:nodoc: | |
| 190 253 | 
             
                  def range_query
         | 
| 191 254 | 
             
                    opts = { :hash_value => query[source.hash_key] }
         | 
| 192 255 | 
             
                    if key = query.keys.find { |k| k.to_s.include?('.') }
         | 
| 193 | 
            -
                      opts.merge!( | 
| 256 | 
            +
                      opts.merge!(range_hash(key))
         | 
| 194 257 | 
             
                    end
         | 
| 195 258 | 
             
                    opts.merge(query_opts).merge(consistent_opts)
         | 
| 196 259 | 
             
                  end
         | 
| @@ -214,15 +277,24 @@ module Dynamoid #:nodoc: | |
| 214 277 | 
             
                  end
         | 
| 215 278 |  | 
| 216 279 | 
             
                  def start_key
         | 
| 217 | 
            -
             | 
| 280 | 
            +
             	      hash_key_type = @start.class.attributes[@start.class.hash_key][:type] == :string ? 'S' : 'N'
         | 
| 281 | 
            +
                    key = { :hash_key_element => { hash_key_type => @start.hash_key.to_s } }
         | 
| 218 282 | 
             
                    if range_key = @start.class.range_key
         | 
| 219 283 | 
             
                      range_key_type = @start.class.attributes[range_key][:type] == :string ? 'S' : 'N'
         | 
| 220 | 
            -
                      key.merge!({:range_key_element => { range_key_type => @start.send(range_key) } })
         | 
| 284 | 
            +
                      key.merge!({:range_key_element => { range_key_type => @start.send(range_key).to_s } })
         | 
| 221 285 | 
             
                    end
         | 
| 222 286 | 
             
                    key
         | 
| 223 287 | 
             
                  end
         | 
| 224 288 |  | 
| 225 289 | 
             
                  def query_opts
         | 
| 290 | 
            +
                    opts = {}
         | 
| 291 | 
            +
                    opts[:limit] = @limit if @limit
         | 
| 292 | 
            +
                    opts[:next_token] = start_key if @start
         | 
| 293 | 
            +
                    opts[:scan_index_forward] = @scan_index_forward
         | 
| 294 | 
            +
                    opts
         | 
| 295 | 
            +
                  end
         | 
| 296 | 
            +
                  
         | 
| 297 | 
            +
                  def scan_opts
         | 
| 226 298 | 
             
                    opts = {}
         | 
| 227 299 | 
             
                    opts[:limit] = @limit if @limit
         | 
| 228 300 | 
             
                    opts[:next_token] = start_key if @start
         | 
    
        data/lib/dynamoid/document.rb
    CHANGED
    
    | @@ -8,8 +8,9 @@ module Dynamoid #:nodoc: | |
| 8 8 | 
             
                include Dynamoid::Components
         | 
| 9 9 |  | 
| 10 10 | 
             
                included do
         | 
| 11 | 
            -
                  class_attribute :options
         | 
| 11 | 
            +
                  class_attribute :options, :read_only_attributes
         | 
| 12 12 | 
             
                  self.options = {}
         | 
| 13 | 
            +
                  self.read_only_attributes = []
         | 
| 13 14 |  | 
| 14 15 | 
             
                  Dynamoid::Config.included_models << self
         | 
| 15 16 | 
             
                end
         | 
| @@ -29,6 +30,10 @@ module Dynamoid #:nodoc: | |
| 29 30 | 
             
                    self.options = options
         | 
| 30 31 | 
             
                  end
         | 
| 31 32 |  | 
| 33 | 
            +
                  def attr_readonly(*read_only_attributes)
         | 
| 34 | 
            +
                    self.read_only_attributes.concat read_only_attributes.map(&:to_s)
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 32 37 | 
             
                  # Returns the read_capacity for this table.
         | 
| 33 38 | 
             
                  #
         | 
| 34 39 | 
             
                  # @since 0.4.0
         | 
    
        data/lib/dynamoid/fields.rb
    CHANGED
    
    | @@ -81,7 +81,23 @@ module Dynamoid #:nodoc: | |
| 81 81 | 
             
                # @since 0.2.0
         | 
| 82 82 | 
             
                def update_attributes(attributes)
         | 
| 83 83 | 
             
                  attributes.each {|attribute, value| self.write_attribute(attribute, value)}
         | 
| 84 | 
            -
                  save
         | 
| 84 | 
            +
                  if self.new_record # if never saved save.
         | 
| 85 | 
            +
                    save
         | 
| 86 | 
            +
                  else # update attributes if we have saved.
         | 
| 87 | 
            +
                    # next if self.read_only_attributes.include? attribute.to_s put this back in.
         | 
| 88 | 
            +
                    run_callbacks(:save) do
         | 
| 89 | 
            +
                      update! do |u|
         | 
| 90 | 
            +
                        attributes.each do |attribute, value|
         | 
| 91 | 
            +
                          u.set attribute => dump_field(
         | 
| 92 | 
            +
                            self.read_attribute(attribute),
         | 
| 93 | 
            +
                            self.class.attributes[attribute.to_sym]
         | 
| 94 | 
            +
                          )
         | 
| 95 | 
            +
                        end
         | 
| 96 | 
            +
                      end
         | 
| 97 | 
            +
                    end
         | 
| 98 | 
            +
                    
         | 
| 99 | 
            +
                    save
         | 
| 100 | 
            +
                  end
         | 
| 85 101 | 
             
                end
         | 
| 86 102 |  | 
| 87 103 | 
             
                # Update a single attribute, saving the object afterwards.
         | 
    
        data/lib/dynamoid/persistence.rb
    CHANGED
    
    | @@ -17,7 +17,7 @@ module Dynamoid | |
| 17 17 | 
             
                  #
         | 
| 18 18 | 
             
                  # @since 0.2.0
         | 
| 19 19 | 
             
                  def table_name
         | 
| 20 | 
            -
                    "#{Dynamoid::Config.namespace}_#{options[:name] ? options[:name] : self.name.downcase.pluralize}"
         | 
| 20 | 
            +
                    "#{Dynamoid::Config.namespace}_#{options[:name] ? options[:name] : self.name.split('::').last.downcase.pluralize}"
         | 
| 21 21 | 
             
                  end
         | 
| 22 22 |  | 
| 23 23 | 
             
                  # Creates a table.
         | 
| @@ -53,7 +53,7 @@ module Dynamoid | |
| 53 53 | 
             
                  #
         | 
| 54 54 | 
             
                  # @since 0.2.0
         | 
| 55 55 | 
             
                  def table_exists?(table_name)
         | 
| 56 | 
            -
                    Dynamoid::Adapter.tables.include?(table_name)
         | 
| 56 | 
            +
                    Dynamoid::Adapter.tables ? Dynamoid::Adapter.tables.include?(table_name) : false
         | 
| 57 57 | 
             
                  end
         | 
| 58 58 |  | 
| 59 59 | 
             
                  def from_database(attrs = {})
         | 
| @@ -109,6 +109,15 @@ module Dynamoid | |
| 109 109 | 
             
                      else
         | 
| 110 110 | 
             
                        value
         | 
| 111 111 | 
             
                      end
         | 
| 112 | 
            +
                    when :boolean
         | 
| 113 | 
            +
                      # persisted as 't', but because undump is called during initialize it can come in as true
         | 
| 114 | 
            +
                      if value == 't' || value == true
         | 
| 115 | 
            +
                        true
         | 
| 116 | 
            +
                      elsif value == 'f' || value == false
         | 
| 117 | 
            +
                        false
         | 
| 118 | 
            +
                      else
         | 
| 119 | 
            +
                        raise ArgumentError, "Boolean column neither true nor false"
         | 
| 120 | 
            +
                      end
         | 
| 112 121 | 
             
                    end
         | 
| 113 122 | 
             
                  end
         | 
| 114 123 |  | 
| @@ -146,9 +155,12 @@ module Dynamoid | |
| 146 155 | 
             
                # @since 0.2.0
         | 
| 147 156 | 
             
                def save(options = {})
         | 
| 148 157 | 
             
                  self.class.create_table
         | 
| 149 | 
            -
             | 
| 158 | 
            +
                  
         | 
| 150 159 | 
             
                  if new_record?
         | 
| 151 | 
            -
                     | 
| 160 | 
            +
                    conditions = { :unless_exists => [self.class.hash_key]}
         | 
| 161 | 
            +
                    conditions[:unless_exists] << range_key if(range_key)
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                    run_callbacks(:create) { persist(conditions) }
         | 
| 152 164 | 
             
                  else
         | 
| 153 165 | 
             
                    persist
         | 
| 154 166 | 
             
                  end
         | 
| @@ -225,6 +237,10 @@ module Dynamoid | |
| 225 237 | 
             
                    value.to_time.to_f
         | 
| 226 238 | 
             
                  when :serialized
         | 
| 227 239 | 
             
                    options[:serializer] ? options[:serializer].dump(value) : value.to_yaml
         | 
| 240 | 
            +
                  when :boolean
         | 
| 241 | 
            +
                    value.to_s[0]
         | 
| 242 | 
            +
                  else
         | 
| 243 | 
            +
                    raise ArgumentError, "Unknown type #{options[:type]}"
         | 
| 228 244 | 
             
                  end
         | 
| 229 245 | 
             
                end
         | 
| 230 246 |  | 
| @@ -232,10 +248,10 @@ module Dynamoid | |
| 232 248 | 
             
                # save its indexes.
         | 
| 233 249 | 
             
                #
         | 
| 234 250 | 
             
                # @since 0.2.0
         | 
| 235 | 
            -
                def persist
         | 
| 251 | 
            +
                def persist(conditions = nil)
         | 
| 236 252 | 
             
                  run_callbacks(:save) do
         | 
| 237 253 | 
             
                    self.hash_key = SecureRandom.uuid if self.hash_key.nil? || self.hash_key.blank?
         | 
| 238 | 
            -
                    Dynamoid::Adapter.write(self.class.table_name, self.dump)
         | 
| 254 | 
            +
                    Dynamoid::Adapter.write(self.class.table_name, self.dump, conditions)
         | 
| 239 255 | 
             
                    save_indexes
         | 
| 240 256 | 
             
                    @new_record = false
         | 
| 241 257 | 
             
                    true
         | 
    
        data/spec/app/models/address.rb
    CHANGED
    
    
| @@ -16,11 +16,12 @@ describe Dynamoid::Adapter::AwsSdk do | |
| 16 16 | 
             
                  end
         | 
| 17 17 | 
             
                end
         | 
| 18 18 |  | 
| 19 | 
            -
                context 'with a preexisting table' do
         | 
| 19 | 
            +
                context 'with a preexisting table without paritioning' do
         | 
| 20 20 | 
             
                  before(:all) do
         | 
| 21 21 | 
             
                    Dynamoid::Adapter.create_table('dynamoid_tests_TestTable1', :id) unless Dynamoid::Adapter.list_tables.include?('dynamoid_tests_TestTable1')
         | 
| 22 22 | 
             
                    Dynamoid::Adapter.create_table('dynamoid_tests_TestTable2', :id) unless Dynamoid::Adapter.list_tables.include?('dynamoid_tests_TestTable2')
         | 
| 23 23 | 
             
                    Dynamoid::Adapter.create_table('dynamoid_tests_TestTable3', :id, :range_key => { :range => :number }) unless Dynamoid::Adapter.list_tables.include?('dynamoid_tests_TestTable3')
         | 
| 24 | 
            +
                    Dynamoid::Adapter.create_table('dynamoid_tests_TestTable4', :id, :range_key => { :range => :number }) unless Dynamoid::Adapter.list_tables.include?('dynamoid_tests_TestTable4')
         | 
| 24 25 | 
             
                  end
         | 
| 25 26 |  | 
| 26 27 | 
             
                  # GetItem, PutItem and DeleteItem
         | 
| @@ -99,6 +100,56 @@ describe Dynamoid::Adapter::AwsSdk do | |
| 99 100 | 
             
                    results['dynamoid_tests_TestTable3'].should include({:name => 'Josh', :id => '1', :range => 1.0})
         | 
| 100 101 | 
             
                    results['dynamoid_tests_TestTable3'].should include({:name => 'Justin', :id => '2', :range => 2.0})
         | 
| 101 102 | 
             
                  end
         | 
| 103 | 
            +
                  
         | 
| 104 | 
            +
                  # BatchDeleteItem
         | 
| 105 | 
            +
                  it "performs BatchDeleteItem with singular keys" do
         | 
| 106 | 
            +
                    Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1', :name => 'Josh'})
         | 
| 107 | 
            +
                    Dynamoid::Adapter.put_item('dynamoid_tests_TestTable2', {:id => '1', :name => 'Justin'})
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                    Dynamoid::Adapter.batch_delete_item('dynamoid_tests_TestTable1' => ['1'], 'dynamoid_tests_TestTable2' => ['1'])
         | 
| 110 | 
            +
                    
         | 
| 111 | 
            +
                    results = Dynamoid::Adapter.batch_get_item('dynamoid_tests_TestTable1' => '1', 'dynamoid_tests_TestTable2' => '1')
         | 
| 112 | 
            +
                    results.size.should == 0
         | 
| 113 | 
            +
                    
         | 
| 114 | 
            +
                    results['dynamoid_tests_TestTable1'].should_not include({:name => 'Josh', :id => '1'})
         | 
| 115 | 
            +
                    results['dynamoid_tests_TestTable2'].should_not include({:name => 'Justin', :id => '1'})
         | 
| 116 | 
            +
                  end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                  it "performs BatchDeleteItem with multiple keys" do
         | 
| 119 | 
            +
                    Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1', :name => 'Josh'})
         | 
| 120 | 
            +
                    Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '2', :name => 'Justin'})
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                    Dynamoid::Adapter.batch_delete_item('dynamoid_tests_TestTable1' => ['1', '2'])
         | 
| 123 | 
            +
                    
         | 
| 124 | 
            +
                    results = Dynamoid::Adapter.batch_get_item('dynamoid_tests_TestTable1' => ['1', '2'])
         | 
| 125 | 
            +
                    results.size.should == 0
         | 
| 126 | 
            +
                    
         | 
| 127 | 
            +
                    results['dynamoid_tests_TestTable1'].should_not include({:name => 'Josh', :id => '1'})
         | 
| 128 | 
            +
                    results['dynamoid_tests_TestTable1'].should_not include({:name => 'Justin', :id => '2'})
         | 
| 129 | 
            +
                  end
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                  it 'performs BatchDeleteItem with one ranged key' do
         | 
| 132 | 
            +
                    Dynamoid::Adapter.put_item('dynamoid_tests_TestTable3', {:id => '1', :name => 'Josh', :range => 1.0})
         | 
| 133 | 
            +
                    Dynamoid::Adapter.put_item('dynamoid_tests_TestTable3', {:id => '2', :name => 'Justin', :range => 2.0})
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                    Dynamoid::Adapter.batch_delete_item('dynamoid_tests_TestTable3' => [['1', 1.0]])
         | 
| 136 | 
            +
                    results = Dynamoid::Adapter.batch_get_item('dynamoid_tests_TestTable3' => [['1', 1.0]])
         | 
| 137 | 
            +
                    results.size.should == 0
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                    results['dynamoid_tests_TestTable3'].should_not include({:name => 'Josh', :id => '1', :range => 1.0})
         | 
| 140 | 
            +
                  end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                  it 'performs BatchDeleteItem with multiple ranged keys' do
         | 
| 143 | 
            +
                    Dynamoid::Adapter.put_item('dynamoid_tests_TestTable3', {:id => '1', :name => 'Josh', :range => 1.0})
         | 
| 144 | 
            +
                    Dynamoid::Adapter.put_item('dynamoid_tests_TestTable3', {:id => '2', :name => 'Justin', :range => 2.0})
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                    Dynamoid::Adapter.batch_delete_item('dynamoid_tests_TestTable3' => [['1', 1.0],['2', 2.0]])
         | 
| 147 | 
            +
                    results = Dynamoid::Adapter.batch_get_item('dynamoid_tests_TestTable3' => [['1', 1.0],['2', 2.0]])
         | 
| 148 | 
            +
                    results.size.should == 0
         | 
| 149 | 
            +
                     
         | 
| 150 | 
            +
                    results['dynamoid_tests_TestTable3'].should_not include({:name => 'Josh', :id => '1', :range => 1.0})
         | 
| 151 | 
            +
                    results['dynamoid_tests_TestTable3'].should_not include({:name => 'Justin', :id => '2', :range => 2.0})
         | 
| 152 | 
            +
                  end
         | 
| 102 153 |  | 
| 103 154 | 
             
                  # ListTables
         | 
| 104 155 | 
             
                  it 'performs ListTables' do
         | 
| @@ -174,6 +225,152 @@ describe Dynamoid::Adapter::AwsSdk do | |
| 174 225 |  | 
| 175 226 | 
             
                    Dynamoid::Adapter.scan('dynamoid_tests_TestTable1', {}).should include({:name=>"Josh", :id=>"2"}, {:name=>"Josh", :id=>"1"})
         | 
| 176 227 | 
             
                  end
         | 
| 228 | 
            +
                  
         | 
| 229 | 
            +
                  context 'correct ordering ' do
         | 
| 230 | 
            +
                    before do
         | 
| 231 | 
            +
                      Dynamoid::Adapter.put_item('dynamoid_tests_TestTable4', {:id => '1', :order => 1, :range => 1.0})
         | 
| 232 | 
            +
                      Dynamoid::Adapter.put_item('dynamoid_tests_TestTable4', {:id => '1', :order => 2, :range => 2.0})
         | 
| 233 | 
            +
                      Dynamoid::Adapter.put_item('dynamoid_tests_TestTable4', {:id => '1', :order => 3, :range => 3.0})
         | 
| 234 | 
            +
                      Dynamoid::Adapter.put_item('dynamoid_tests_TestTable4', {:id => '1', :order => 4, :range => 4.0})
         | 
| 235 | 
            +
                      Dynamoid::Adapter.put_item('dynamoid_tests_TestTable4', {:id => '1', :order => 5, :range => 5.0})
         | 
| 236 | 
            +
                      Dynamoid::Adapter.put_item('dynamoid_tests_TestTable4', {:id => '1', :order => 6, :range => 6.0})
         | 
| 237 | 
            +
                    end
         | 
| 238 | 
            +
             | 
| 239 | 
            +
                    it 'performs query on a table with a range and selects items less than that is in the correct order, scan_index_forward true' do
         | 
| 240 | 
            +
                      query = Dynamoid::Adapter.query('dynamoid_tests_TestTable4', :hash_value => '1', :range_greater_than => 0, :scan_index_forward => true)
         | 
| 241 | 
            +
                      query[0].should == {:id => '1', :order => 1, :range => BigDecimal.new(1)}
         | 
| 242 | 
            +
                      query[1].should == {:id => '1', :order => 2, :range => BigDecimal.new(2)}
         | 
| 243 | 
            +
                      query[2].should == {:id => '1', :order => 3, :range => BigDecimal.new(3)}
         | 
| 244 | 
            +
                      query[3].should == {:id => '1', :order => 4, :range => BigDecimal.new(4)}
         | 
| 245 | 
            +
                      query[4].should == {:id => '1', :order => 5, :range => BigDecimal.new(5)}
         | 
| 246 | 
            +
                      query[5].should == {:id => '1', :order => 6, :range => BigDecimal.new(6)}
         | 
| 247 | 
            +
                    end
         | 
| 248 | 
            +
                    
         | 
| 249 | 
            +
                    it 'performs query on a table with a range and selects items less than that is in the correct order, scan_index_forward false' do
         | 
| 250 | 
            +
                      query = Dynamoid::Adapter.query('dynamoid_tests_TestTable4', :hash_value => '1', :range_greater_than => 0, :scan_index_forward => false)
         | 
| 251 | 
            +
                      query[5].should == {:id => '1', :order => 1, :range => BigDecimal.new(1)}
         | 
| 252 | 
            +
                      query[4].should == {:id => '1', :order => 2, :range => BigDecimal.new(2)}
         | 
| 253 | 
            +
                      query[3].should == {:id => '1', :order => 3, :range => BigDecimal.new(3)}
         | 
| 254 | 
            +
                      query[2].should == {:id => '1', :order => 4, :range => BigDecimal.new(4)}
         | 
| 255 | 
            +
                      query[1].should == {:id => '1', :order => 5, :range => BigDecimal.new(5)}
         | 
| 256 | 
            +
                      query[0].should == {:id => '1', :order => 6, :range => BigDecimal.new(6)}
         | 
| 257 | 
            +
                    end
         | 
| 258 | 
            +
                  end
         | 
| 259 | 
            +
                end
         | 
| 260 | 
            +
                
         | 
| 261 | 
            +
                context 'with a preexisting table with paritioning' do
         | 
| 262 | 
            +
                  before(:all) do
         | 
| 263 | 
            +
                    @previous_value = Dynamoid::Config.partitioning
         | 
| 264 | 
            +
                    Dynamoid::Config.partitioning = true
         | 
| 265 | 
            +
                    
         | 
| 266 | 
            +
                    Dynamoid::Adapter.create_table('dynamoid_tests_TestTable1', :id) unless Dynamoid::Adapter.list_tables.include?('dynamoid_tests_TestTable1')
         | 
| 267 | 
            +
                    Dynamoid::Adapter.create_table('dynamoid_tests_TestTable2', :id) unless Dynamoid::Adapter.list_tables.include?('dynamoid_tests_TestTable2')
         | 
| 268 | 
            +
                    Dynamoid::Adapter.create_table('dynamoid_tests_TestTable3', :id, :range_key => { :range => :number }) unless Dynamoid::Adapter.list_tables.include?('dynamoid_tests_TestTable3')
         | 
| 269 | 
            +
                  end
         | 
| 270 | 
            +
             | 
| 271 | 
            +
                  after(:all) do
         | 
| 272 | 
            +
                    Dynamoid::Config.partitioning = @previous_value
         | 
| 273 | 
            +
                  end
         | 
| 274 | 
            +
             | 
| 275 | 
            +
                  # Query
         | 
| 276 | 
            +
                  it 'performs query on a table and returns items' do
         | 
| 277 | 
            +
                    Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1.1', :name => 'Josh'})
         | 
| 278 | 
            +
             | 
| 279 | 
            +
                    Dynamoid::Adapter.query('dynamoid_tests_TestTable1', :hash_value => '1').first.should == { :id=> '1', :name=>"Josh" }
         | 
| 280 | 
            +
                  end
         | 
| 281 | 
            +
             | 
| 282 | 
            +
                  it 'performs query on a table and returns items if there are multiple items' do
         | 
| 283 | 
            +
                    Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1.1', :name => 'Josh'})
         | 
| 284 | 
            +
                    Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '2.1', :name => 'Justin'})
         | 
| 285 | 
            +
             | 
| 286 | 
            +
                    Dynamoid::Adapter.query('dynamoid_tests_TestTable1', :hash_value => '1').first.should == { :id=> '1', :name=>"Josh" }
         | 
| 287 | 
            +
                  end
         | 
| 288 | 
            +
             | 
| 289 | 
            +
                  context 'range queries' do
         | 
| 290 | 
            +
                    before do
         | 
| 291 | 
            +
                      Dynamoid::Adapter.put_item('dynamoid_tests_TestTable3', {:id => '1.1', :range => 1.0})
         | 
| 292 | 
            +
                      Dynamoid::Adapter.put_item('dynamoid_tests_TestTable3', {:id => '1.1', :range => 3.0})
         | 
| 293 | 
            +
                    end
         | 
| 294 | 
            +
             | 
| 295 | 
            +
                    it 'performs query on a table with a range and selects items in a range' do
         | 
| 296 | 
            +
                      Dynamoid::Adapter.query('dynamoid_tests_TestTable3', :hash_value => '1', :range_value => 0.0..3.0).should =~ [{:id => '1', :range => BigDecimal.new(1)}, {:id => '1', :range => BigDecimal.new(3)}]
         | 
| 297 | 
            +
                    end
         | 
| 298 | 
            +
             | 
| 299 | 
            +
                    it 'performs query on a table with a range and selects items greater than' do
         | 
| 300 | 
            +
                      Dynamoid::Adapter.query('dynamoid_tests_TestTable3', :hash_value => '1', :range_greater_than => 1.0).should =~ [{:id => '1', :range => BigDecimal.new(3)}]
         | 
| 301 | 
            +
                    end
         | 
| 302 | 
            +
             | 
| 303 | 
            +
                    it 'performs query on a table with a range and selects items less than' do
         | 
| 304 | 
            +
                      Dynamoid::Adapter.query('dynamoid_tests_TestTable3', :hash_value => '1', :range_less_than => 2.0).should =~ [{:id => '1', :range => BigDecimal.new(1)}]
         | 
| 305 | 
            +
                    end
         | 
| 306 | 
            +
             | 
| 307 | 
            +
                    it 'performs query on a table with a range and selects items gte' do
         | 
| 308 | 
            +
                      Dynamoid::Adapter.query('dynamoid_tests_TestTable3', :hash_value => '1', :range_gte => 1.0).should =~ [{:id => '1', :range => BigDecimal.new(1)}, {:id => '1', :range => BigDecimal.new(3)}]
         | 
| 309 | 
            +
                    end
         | 
| 310 | 
            +
             | 
| 311 | 
            +
                    it 'performs query on a table with a range and selects items lte' do
         | 
| 312 | 
            +
                      Dynamoid::Adapter.query('dynamoid_tests_TestTable3', :hash_value => '1', :range_lte => 3.0).should =~ [{:id => '1', :range => BigDecimal.new(1)}, {:id => '1', :range => BigDecimal.new(3)}]
         | 
| 313 | 
            +
                    end
         | 
| 314 | 
            +
                  end
         | 
| 315 | 
            +
             | 
| 316 | 
            +
                  # Scan
         | 
| 317 | 
            +
                  it 'performs scan on a table and returns items' do
         | 
| 318 | 
            +
                    Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1.1', :name => 'Josh'})
         | 
| 319 | 
            +
             | 
| 320 | 
            +
                    Dynamoid::Adapter.scan('dynamoid_tests_TestTable1', :name => 'Josh').should == [{ :id=> '1', :name=>"Josh" }]
         | 
| 321 | 
            +
                  end
         | 
| 322 | 
            +
             | 
| 323 | 
            +
                  it 'performs scan on a table and returns items if there are multiple items but only one match' do
         | 
| 324 | 
            +
                    Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1.1', :name => 'Josh'})
         | 
| 325 | 
            +
                    Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '2.1', :name => 'Justin'})
         | 
| 326 | 
            +
             | 
| 327 | 
            +
                    Dynamoid::Adapter.scan('dynamoid_tests_TestTable1', :name => 'Josh').should == [{ :id=> '1', :name=>"Josh" }]
         | 
| 328 | 
            +
                  end
         | 
| 329 | 
            +
             | 
| 330 | 
            +
                  it 'performs scan on a table and returns multiple items if there are multiple matches' do
         | 
| 331 | 
            +
                    Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1.1', :name => 'Josh'})
         | 
| 332 | 
            +
                    Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '2.1', :name => 'Josh'})
         | 
| 333 | 
            +
             | 
| 334 | 
            +
                    Dynamoid::Adapter.scan('dynamoid_tests_TestTable1', :name => 'Josh').should include({:name=>"Josh", :id=>"2"}, {:name=>"Josh", :id=>"1"})
         | 
| 335 | 
            +
                  end
         | 
| 336 | 
            +
             | 
| 337 | 
            +
                  it 'performs scan on a table and returns all items if no criteria are specified' do
         | 
| 338 | 
            +
                    Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1.1', :name => 'Josh'})
         | 
| 339 | 
            +
                    Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '2.1', :name => 'Josh'})
         | 
| 340 | 
            +
             | 
| 341 | 
            +
                    Dynamoid::Adapter.scan('dynamoid_tests_TestTable1', {}).should include({:name=>"Josh", :id=>"2"}, {:name=>"Josh", :id=>"1"})
         | 
| 342 | 
            +
                  end
         | 
| 343 | 
            +
                  
         | 
| 344 | 
            +
                  context 'correct ordering ' do
         | 
| 345 | 
            +
                    before do
         | 
| 346 | 
            +
                      Dynamoid::Adapter.put_item('dynamoid_tests_TestTable4', {:id => '1.1', :range => 1.0})
         | 
| 347 | 
            +
                      Dynamoid::Adapter.put_item('dynamoid_tests_TestTable4', {:id => '1.2', :range => 2.0})
         | 
| 348 | 
            +
                      Dynamoid::Adapter.put_item('dynamoid_tests_TestTable4', {:id => '1.3', :range => 3.0})
         | 
| 349 | 
            +
                      Dynamoid::Adapter.put_item('dynamoid_tests_TestTable4', {:id => '1.4', :range => 4.0})
         | 
| 350 | 
            +
                      Dynamoid::Adapter.put_item('dynamoid_tests_TestTable4', {:id => '1.5', :range => 5.0})
         | 
| 351 | 
            +
                      Dynamoid::Adapter.put_item('dynamoid_tests_TestTable4', {:id => '1.6', :range => 6.0})
         | 
| 352 | 
            +
                    end
         | 
| 353 | 
            +
             | 
| 354 | 
            +
                    it 'performs query on a table with a range and selects items less than that is in the correct order, scan_index_forward true' do
         | 
| 355 | 
            +
                      query = Dynamoid::Adapter.query('dynamoid_tests_TestTable4', :hash_value => '1', :range_greater_than => 0, :scan_index_forward => true)
         | 
| 356 | 
            +
                      query[0].should == {:id => '1', :range => BigDecimal.new(1)}
         | 
| 357 | 
            +
                      query[1].should == {:id => '1', :range => BigDecimal.new(2)}
         | 
| 358 | 
            +
                      query[2].should == {:id => '1', :range => BigDecimal.new(3)}
         | 
| 359 | 
            +
                      query[3].should == {:id => '1', :range => BigDecimal.new(4)}
         | 
| 360 | 
            +
                      query[4].should == {:id => '1', :range => BigDecimal.new(5)}
         | 
| 361 | 
            +
                      query[5].should == {:id => '1', :range => BigDecimal.new(6)}
         | 
| 362 | 
            +
                    end
         | 
| 363 | 
            +
                    
         | 
| 364 | 
            +
                    it 'performs query on a table with a range and selects items less than that is in the correct order, scan_index_forward false' do
         | 
| 365 | 
            +
                      query = Dynamoid::Adapter.query('dynamoid_tests_TestTable4', :hash_value => '1', :range_greater_than => 0, :scan_index_forward => false)
         | 
| 366 | 
            +
                      query[5].should == {:id => '1', :range => BigDecimal.new(1)}
         | 
| 367 | 
            +
                      query[4].should == {:id => '1', :range => BigDecimal.new(2)}
         | 
| 368 | 
            +
                      query[3].should == {:id => '1', :range => BigDecimal.new(3)}
         | 
| 369 | 
            +
                      query[2].should == {:id => '1', :range => BigDecimal.new(4)}
         | 
| 370 | 
            +
                      query[1].should == {:id => '1', :range => BigDecimal.new(5)}
         | 
| 371 | 
            +
                      query[0].should == {:id => '1', :range => BigDecimal.new(6)}
         | 
| 372 | 
            +
                    end
         | 
| 373 | 
            +
                  end
         | 
| 177 374 | 
             
                end
         | 
| 178 375 |  | 
| 179 376 | 
             
                # DescribeTable
         | 
| @@ -25,7 +25,7 @@ describe "Dynamoid::Adapter" do | |
| 25 25 | 
             
                end
         | 
| 26 26 |  | 
| 27 27 | 
             
                it 'writes through the adapter' do
         | 
| 28 | 
            -
                  Dynamoid::Adapter.expects(:put_item).with('dynamoid_tests_TestTable', {:id => '123'}).returns(true)
         | 
| 28 | 
            +
                  Dynamoid::Adapter.expects(:put_item).with('dynamoid_tests_TestTable', {:id => '123'}, nil).returns(true)
         | 
| 29 29 |  | 
| 30 30 | 
             
                  Dynamoid::Adapter.write('dynamoid_tests_TestTable', {:id => '123'})
         | 
| 31 31 | 
             
                end
         | 
| @@ -42,6 +42,18 @@ describe "Dynamoid::Adapter" do | |
| 42 42 | 
             
                  Dynamoid::Adapter.read('dynamoid_tests_TestTable', ['1', '2'])
         | 
| 43 43 | 
             
                end
         | 
| 44 44 |  | 
| 45 | 
            +
                it 'delete through the adapter for one ID' do
         | 
| 46 | 
            +
                  Dynamoid::Adapter.expects(:delete_item).with('dynamoid_tests_TestTable', '123', {}).returns(nil)
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  Dynamoid::Adapter.delete('dynamoid_tests_TestTable', '123')
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
                
         | 
| 51 | 
            +
                it 'deletes through the adapter for many IDs' do
         | 
| 52 | 
            +
                  Dynamoid::Adapter.expects(:batch_delete_item).with({'dynamoid_tests_TestTable' => ['1', '2']}).returns(nil)
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  Dynamoid::Adapter.delete('dynamoid_tests_TestTable', ['1', '2'])
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
                
         | 
| 45 57 | 
             
                it 'reads through the adapter for one ID and a range key' do
         | 
| 46 58 | 
             
                  Dynamoid::Adapter.expects(:get_item).with('dynamoid_tests_TestTable', '123', :range_key => 2.0).returns(true)
         | 
| 47 59 |  | 
| @@ -53,6 +65,18 @@ describe "Dynamoid::Adapter" do | |
| 53 65 |  | 
| 54 66 | 
             
                  Dynamoid::Adapter.read('dynamoid_tests_TestTable', ['1', '2'], :range_key => 2.0)
         | 
| 55 67 | 
             
                end
         | 
| 68 | 
            +
                
         | 
| 69 | 
            +
                it 'deletes through the adapter for one ID and a range key' do
         | 
| 70 | 
            +
                  Dynamoid::Adapter.expects(:delete_item).with('dynamoid_tests_TestTable', '123', :range_key => 2.0).returns(nil)
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  Dynamoid::Adapter.delete('dynamoid_tests_TestTable', '123', :range_key => 2.0)      
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
                
         | 
| 75 | 
            +
                it 'deletes through the adapter for many IDs and a range key' do
         | 
| 76 | 
            +
                  Dynamoid::Adapter.expects(:batch_delete_item).with({'dynamoid_tests_TestTable' => [['1', 2.0], ['2', 2.0]]}).returns(nil)
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  Dynamoid::Adapter.delete('dynamoid_tests_TestTable', ['1', '2'], :range_key => [2.0,2.0])
         | 
| 79 | 
            +
                end
         | 
| 56 80 | 
             
              end
         | 
| 57 81 |  | 
| 58 82 | 
             
              context 'with partitioning' do
         | 
| @@ -109,9 +133,38 @@ describe "Dynamoid::Adapter" do | |
| 109 133 | 
             
                  @time = DateTime.now
         | 
| 110 134 | 
             
                  @array =[{:id => '1.0', :updated_at => @time - 6.hours}, {:id => '1.1', :updated_at => @time - 3.hours}, {:id => '1.2', :updated_at => @time - 1.hour}, {:id => '1.3', :updated_at => @time - 6.hours}, {:id => '2.0', :updated_at => @time}]
         | 
| 111 135 |  | 
| 112 | 
            -
                  Dynamoid::Adapter.result_for_partition(@array).should =~ [{:id => '1', :updated_at => @time - 1.hour}, {:id => '2', :updated_at => @time}]
         | 
| 136 | 
            +
                  Dynamoid::Adapter.result_for_partition(@array,"dynamoid_tests_TestTable").should =~ [{:id => '1', :updated_at => @time - 1.hour}, {:id => '2', :updated_at => @time}]
         | 
| 113 137 | 
             
                end
         | 
| 114 138 |  | 
| 139 | 
            +
                it 'returns a valid original id and partition number' do
         | 
| 140 | 
            +
                  @id = "12345.387327.-sdf3"
         | 
| 141 | 
            +
                  @partition_number = "4"
         | 
| 142 | 
            +
                  Dynamoid::Adapter.get_original_id_and_partition("#{@id}.#{@partition_number}").should == [@id, @partition_number]
         | 
| 143 | 
            +
                end
         | 
| 144 | 
            +
                
         | 
| 145 | 
            +
                it 'delete through the adapter for one ID' do
         | 
| 146 | 
            +
                  Dynamoid::Adapter.expects(:batch_delete_item).with('dynamoid_tests_TestTable' => (0...Dynamoid::Config.partition_size).collect{|n| "123.#{n}"}).returns(nil)
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                  Dynamoid::Adapter.delete('dynamoid_tests_TestTable', '123')
         | 
| 149 | 
            +
                end
         | 
| 150 | 
            +
                
         | 
| 151 | 
            +
                it 'deletes through the adapter for many IDs' do
         | 
| 152 | 
            +
                  Dynamoid::Adapter.expects(:batch_delete_item).with('dynamoid_tests_TestTable' => (0...Dynamoid::Config.partition_size).collect{|n| "1.#{n}"} + (0...Dynamoid::Config.partition_size).collect{|n| "2.#{n}"}).returns(nil)
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                  Dynamoid::Adapter.delete('dynamoid_tests_TestTable', ['1', '2'])
         | 
| 155 | 
            +
                end
         | 
| 156 | 
            +
                
         | 
| 157 | 
            +
                it 'deletes through the adapter for one ID and a range key' do
         | 
| 158 | 
            +
                  Dynamoid::Adapter.expects(:batch_delete_item).with('dynamoid_tests_TestTable' => (0...Dynamoid::Config.partition_size).collect{|n| ["123.#{n}", 2.0]}).returns(nil)
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                  Dynamoid::Adapter.delete('dynamoid_tests_TestTable', '123', :range_key => 2.0)      
         | 
| 161 | 
            +
                end
         | 
| 162 | 
            +
                
         | 
| 163 | 
            +
                it 'deletes through the adapter for many IDs and a range key' do
         | 
| 164 | 
            +
                  Dynamoid::Adapter.expects(:batch_delete_item).with('dynamoid_tests_TestTable' => (0...Dynamoid::Config.partition_size).collect{|n| ["1.#{n}", 2.0]} + (0...Dynamoid::Config.partition_size).collect{|n| ["2.#{n}", 2.0]}).returns(nil)
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                  Dynamoid::Adapter.delete('dynamoid_tests_TestTable', ['1', '2'], :range_key => [2.0,2.0])
         | 
| 167 | 
            +
                end
         | 
| 115 168 | 
             
              end
         | 
| 116 169 |  | 
| 117 170 | 
             
            end
         | 
| @@ -136,5 +136,28 @@ describe "Dynamoid::Associations::Chain" do | |
| 136 136 | 
             
                  @chain.send(:records_with_range).should == [@tweet3]
         | 
| 137 137 | 
             
                end
         | 
| 138 138 | 
             
              end
         | 
| 139 | 
            +
              
         | 
| 140 | 
            +
              context 'destroy alls' do
         | 
| 141 | 
            +
                before do
         | 
| 142 | 
            +
                  @tweet1 = Tweet.create(:tweet_id => "x", :group => "one")
         | 
| 143 | 
            +
                  @tweet2 = Tweet.create(:tweet_id => "x", :group => "two")
         | 
| 144 | 
            +
                  @tweet3 = Tweet.create(:tweet_id => "xx", :group => "two")
         | 
| 145 | 
            +
                  @chain = Dynamoid::Criteria::Chain.new(Tweet)
         | 
| 146 | 
            +
                end
         | 
| 147 | 
            +
                
         | 
| 148 | 
            +
                it 'destroys tweet with a range simple range query' do
         | 
| 149 | 
            +
                  @chain.query = { :tweet_id => "x" }
         | 
| 150 | 
            +
                  @chain.all.size.should == 2
         | 
| 151 | 
            +
                  @chain.destroy_all
         | 
| 152 | 
            +
                  @chain.consistent.all.size.should == 0
         | 
| 153 | 
            +
                end
         | 
| 139 154 |  | 
| 155 | 
            +
                it 'deletes one specific tweet with range' do
         | 
| 156 | 
            +
                  @chain = Dynamoid::Criteria::Chain.new(Tweet)
         | 
| 157 | 
            +
                  @chain.query = { :tweet_id => "xx", :group => "two" }
         | 
| 158 | 
            +
                  @chain.all.size.should == 1
         | 
| 159 | 
            +
                  @chain.destroy_all
         | 
| 160 | 
            +
                  @chain.consistent.all.size.should == 0
         | 
| 161 | 
            +
                end
         | 
| 162 | 
            +
              end
         | 
| 140 163 | 
             
            end
         | 
| @@ -6,7 +6,7 @@ describe "Dynamoid::Document" do | |
| 6 6 | 
             
                @address = Address.new
         | 
| 7 7 |  | 
| 8 8 | 
             
                @address.new_record.should be_true
         | 
| 9 | 
            -
                @address.attributes.should == {:id=>nil, :created_at=>nil, :updated_at=>nil, :city=>nil, :options=>nil}
         | 
| 9 | 
            +
                @address.attributes.should == {:id=>nil, :created_at=>nil, :updated_at=>nil, :city=>nil, :options=>nil, :deliverable => nil}
         | 
| 10 10 | 
             
              end
         | 
| 11 11 |  | 
| 12 12 | 
             
              it 'responds to will_change! methods for all fields' do
         | 
| @@ -22,7 +22,7 @@ describe "Dynamoid::Document" do | |
| 22 22 |  | 
| 23 23 | 
             
                @address.new_record.should be_true
         | 
| 24 24 |  | 
| 25 | 
            -
                @address.attributes.should == {:id=>nil, :created_at=>nil, :updated_at=>nil, :city=>"Chicago", :options=>nil}
         | 
| 25 | 
            +
                @address.attributes.should == {:id=>nil, :created_at=>nil, :updated_at=>nil, :city=>"Chicago", :options=>nil, :deliverable => nil}
         | 
| 26 26 | 
             
              end
         | 
| 27 27 |  | 
| 28 28 | 
             
              it 'initializes a new document with a virtual attribute' do
         | 
| @@ -30,7 +30,7 @@ describe "Dynamoid::Document" do | |
| 30 30 |  | 
| 31 31 | 
             
                @address.new_record.should be_true
         | 
| 32 32 |  | 
| 33 | 
            -
                @address.attributes.should == {:id=>nil, :created_at=>nil, :updated_at=>nil, :city=>"Chicago", :options=>nil}
         | 
| 33 | 
            +
                @address.attributes.should == {:id=>nil, :created_at=>nil, :updated_at=>nil, :city=>"Chicago", :options=>nil, :deliverable => nil}
         | 
| 34 34 | 
             
              end
         | 
| 35 35 |  | 
| 36 36 | 
             
              it 'allows interception of write_attribute on load' do
         | 
| @@ -36,13 +36,13 @@ describe "Dynamoid::Fields" do | |
| 36 36 | 
             
                @address.updated_at.should_not be_nil
         | 
| 37 37 | 
             
                @address.updated_at.class.should == DateTime
         | 
| 38 38 | 
             
              end
         | 
| 39 | 
            -
             | 
| 39 | 
            +
              
         | 
| 40 40 | 
             
              context 'with a saved address' do
         | 
| 41 41 | 
             
                before do
         | 
| 42 | 
            -
                  @address = Address.create
         | 
| 42 | 
            +
                  @address = Address.create(:deliverable => true)
         | 
| 43 43 | 
             
                  @original_id = @address.id
         | 
| 44 44 | 
             
                end
         | 
| 45 | 
            -
             | 
| 45 | 
            +
                
         | 
| 46 46 | 
             
                it 'should write an attribute correctly' do
         | 
| 47 47 | 
             
                  @address.write_attribute(:city, 'Chicago')
         | 
| 48 48 | 
             
                end
         | 
| @@ -85,7 +85,7 @@ describe "Dynamoid::Fields" do | |
| 85 85 | 
             
                end
         | 
| 86 86 |  | 
| 87 87 | 
             
                it 'returns all attributes' do
         | 
| 88 | 
            -
                  Address.attributes.should == {:id=>{:type=>:string}, :created_at=>{:type=>:datetime}, :updated_at=>{:type=>:datetime}, :city=>{:type=>:string}, :options=>{:type=>:serialized}}
         | 
| 88 | 
            +
                  Address.attributes.should == {:id=>{:type=>:string}, :created_at=>{:type=>:datetime}, :updated_at=>{:type=>:datetime}, :city=>{:type=>:string}, :options=>{:type=>:serialized}, :deliverable => {:type => :boolean}}
         | 
| 89 89 | 
             
                end
         | 
| 90 90 | 
             
              end
         | 
| 91 91 |  | 
| @@ -89,6 +89,20 @@ describe "Dynamoid::Persistence" do | |
| 89 89 | 
             
                @address.options = (hash = {:x => [1, 2], "foobar" => 3.14})
         | 
| 90 90 | 
             
                Address.undump(@address.send(:dump))[:options].should == hash
         | 
| 91 91 | 
             
              end
         | 
| 92 | 
            +
              
         | 
| 93 | 
            +
              it 'dumps a boolean field' do
         | 
| 94 | 
            +
                @address.deliverable = true
         | 
| 95 | 
            +
                Address.undump(@address.send(:dump))[:deliverable].should == true
         | 
| 96 | 
            +
              end
         | 
| 97 | 
            +
              
         | 
| 98 | 
            +
              it 'raises on an invalid boolean value' do
         | 
| 99 | 
            +
                expect do
         | 
| 100 | 
            +
                  @address.deliverable = true
         | 
| 101 | 
            +
                  data = @address.send(:dump)
         | 
| 102 | 
            +
                  data[:deliverable] = 'foo'
         | 
| 103 | 
            +
                  Address.undump(data)
         | 
| 104 | 
            +
                end.to raise_error(ArgumentError)
         | 
| 105 | 
            +
              end
         | 
| 92 106 |  | 
| 93 107 | 
             
              it 'loads a hash into a serialized field' do
         | 
| 94 108 | 
             
                hash = {foo: :bar}
         | 
| @@ -126,6 +140,38 @@ describe "Dynamoid::Persistence" do | |
| 126 140 |  | 
| 127 141 | 
             
                lambda {Address.create(hash)}.should_not raise_error
         | 
| 128 142 | 
             
              end
         | 
| 143 | 
            +
              
         | 
| 144 | 
            +
              context 'create' do
         | 
| 145 | 
            +
                {
         | 
| 146 | 
            +
                  Tweet   => ['with range',    { :tweet_id => 1, :group => 'abc' }],
         | 
| 147 | 
            +
                  Message => ['without range', { :message_id => 1, :text => 'foo', :time => DateTime.now }]
         | 
| 148 | 
            +
                }.each_pair do |clazz, fields|
         | 
| 149 | 
            +
                  it "checks for existence of an existing object #{fields[0]}" do
         | 
| 150 | 
            +
                    t1 = clazz.new(fields[1])
         | 
| 151 | 
            +
                    t2 = clazz.new(fields[1])
         | 
| 152 | 
            +
                  
         | 
| 153 | 
            +
                    t1.save
         | 
| 154 | 
            +
                    expect do
         | 
| 155 | 
            +
                      t2.save!
         | 
| 156 | 
            +
                    end.to raise_exception AWS::DynamoDB::Errors::ConditionalCheckFailedException
         | 
| 157 | 
            +
                  end
         | 
| 158 | 
            +
                end
         | 
| 159 | 
            +
              end
         | 
| 160 | 
            +
             | 
| 161 | 
            +
              it 'raises when dumping a column with an unknown field type' do
         | 
| 162 | 
            +
                clazz = Class.new do
         | 
| 163 | 
            +
                  include Dynamoid::Document
         | 
| 164 | 
            +
                  table :name => :addresses
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                  field :city
         | 
| 167 | 
            +
                  field :options, :serialized
         | 
| 168 | 
            +
                  field :deliverable, :bad_type_specifier
         | 
| 169 | 
            +
                end
         | 
| 170 | 
            +
                
         | 
| 171 | 
            +
                expect do
         | 
| 172 | 
            +
                  clazz.new(:deliverable => true).dump
         | 
| 173 | 
            +
                end.to raise_error(ArgumentError)
         | 
| 174 | 
            +
              end
         | 
| 129 175 |  | 
| 130 176 | 
             
              context 'update' do
         | 
| 131 177 |  | 
    
        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: 0. | 
| 4 | 
            +
              version: 0.6.0
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
            platform: ruby
         | 
| 7 7 | 
             
            authors:
         | 
| @@ -9,7 +9,7 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2012- | 
| 12 | 
            +
            date: 2012-12-19 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: activemodel
         | 
| @@ -362,7 +362,7 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 362 362 | 
             
                  version: '0'
         | 
| 363 363 | 
             
                  segments:
         | 
| 364 364 | 
             
                  - 0
         | 
| 365 | 
            -
                  hash:  | 
| 365 | 
            +
                  hash: 1769474617671935621
         | 
| 366 366 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 367 367 | 
             
              none: false
         | 
| 368 368 | 
             
              requirements:
         | 
| @@ -371,7 +371,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 371 371 | 
             
                  version: '0'
         | 
| 372 372 | 
             
            requirements: []
         | 
| 373 373 | 
             
            rubyforge_project: 
         | 
| 374 | 
            -
            rubygems_version: 1.8. | 
| 374 | 
            +
            rubygems_version: 1.8.24
         | 
| 375 375 | 
             
            signing_key: 
         | 
| 376 376 | 
             
            specification_version: 3
         | 
| 377 377 | 
             
            summary: Dynamoid is an ORM for Amazon's DynamoDB
         |