Git Product home page Git Product logo

dynamoid's Introduction

Dynamoid

Gem Version Supported Build Status Maintainability Coveralls CodeCov Helpers Contributors RubyDoc.info License GitMoji SemVer 2.0.0 Keep-A-Changelog 1.0.0 Sponsor Project

Dynamoid is an ORM for Amazon's DynamoDB for Ruby applications. It provides similar functionality to ActiveRecord and improves on Amazon's existing HashModel by providing better searching tools and native association support.

DynamoDB is not like other document-based databases you might know, and is very different indeed from relational databases. It sacrifices anything beyond the simplest relational queries and transactional support to provide a fast, cost-efficient, and highly durable storage solution. If your database requires complicated relational queries and transaction support, then this modest Gem cannot provide them for you, and neither can DynamoDB. In those cases you would do better to look elsewhere for your database needs.

But if you want a fast, scalable, simple, easy-to-use database (and a Gem that supports it) then look no further!

Installation

Installing Dynamoid is pretty simple. First include the Gem in your Gemfile:

gem 'dynamoid'

Prerequisites

Dynamoid depends on the aws-sdk, and this is tested on the current version of aws-sdk (~> 3), rails (>= 4). Hence the configuration as needed for aws to work will be dealt with by aws setup.

AWS SDK Version Compatibility

Make sure you are using the version for the right AWS SDK.

Dynamoid version AWS SDK Version
0.x 1.x
1.x 2.x
2.x 2.x
3.x 3.x

AWS Configuration

Configure AWS access: Reference

For example, to configure AWS access:

Create config/initializers/aws.rb as follows:

Aws.config.update(
  region: 'us-west-2',
  credentials: Aws::Credentials.new('REPLACE_WITH_ACCESS_KEY_ID', 'REPLACE_WITH_SECRET_ACCESS_KEY'),
)

Alternatively, if you don't want Aws connection settings to be overwritten for you entire project, you can specify connection settings for Dynamoid only, by setting those in the Dynamoid.configure clause:

require 'dynamoid'
Dynamoid.configure do |config|
  config.access_key = 'REPLACE_WITH_ACCESS_KEY_ID'
  config.secret_key = 'REPLACE_WITH_SECRET_ACCESS_KEY'
  config.region = 'us-west-2'
end

Additionally, if you would like to pass in pre-configured AWS credentials (e.g. you have an IAM role credential, you configure your credentials elsewhere in your project, etc.), you may do so:

require 'dynamoid'

credentials = Aws::AssumeRoleCredentials.new(
  region: region,
  access_key_id: key,
  secret_access_key: secret,
  role_arn: role_arn,
  role_session_name: 'our-session'
)

Dynamoid.configure do |config|
  config.region = 'us-west-2',
                  config.credentials = credentials
end

For a full list of the DDB regions, you can go here.

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):

require 'dynamoid'
Dynamoid.configure do |config|
  # To namespace tables created by Dynamoid from other tables you might have.
  # Set to nil to avoid namespacing.
  config.namespace = 'dynamoid_app_development'

  # [Optional]. If provided, it communicates with the DB listening at the endpoint.
  # This is useful for testing with [DynamoDB Local] (http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Tools.DynamoDBLocal.html).
  config.endpoint = 'http://localhost:3000'
end

Ruby & Rails Compatibility

Dynamoid supports Ruby >= 2.3 and Rails >= 4.2.

Its compatibility is tested against following Ruby versions: 2.3, 2.4, 2.5, 2.6, 2.7, 3.0, 3.1, 3.2 and 3.3, JRuby 9.4.x and against Rails versions: 4.2, 5.0, 5.1, 5.2, 6.0, 6.1, 7.0 and 7.1.

Setup

You must include Dynamoid::Document in every Dynamoid model.

class User
  include Dynamoid::Document

  # fields declaration
end

Table

Dynamoid has some sensible defaults for you when you create a new table, including the table name and the primary key column. But you can change those if you like on table creation.

class User
  include Dynamoid::Document

  table name: :awesome_users, key: :user_id, read_capacity: 5, write_capacity: 5
end

These fields will not change an existing table: so specifying a new read_capacity and write_capacity here only works correctly for entirely new tables. Similarly, while Dynamoid will look for a table named awesome_users in your namespace, it won't change any existing tables to use that name; and if it does find a table with the correct name, it won't change its hash key, which it expects will be user_id. If this table doesn't exist yet, however, Dynamoid will create it with these options.

There is a basic support of DynamoDB's Time To Live (TTL) mechanism. If you declare a field as TTL field - it will be initialised if doesn't have value yet. Default value is current time + specified seconds.

class User
  include Dynamoid::Document

  table expires: { field: :ttl, after: 60 }

  field :ttl, :integer
end

Field used to store expiration time (e.g. ttl) should be declared explicitly and should have numeric type (integer, number) only. datetime type is also possible but only if it's stored as number (there is a way to store time as a string also).

It's also possible to override a global option Dynamoid::Config.timestamps on a table level:

table timestamps: false

This option controls generation of timestamp fields created_at/updated_at.

It's also possible to override table capacity mode configured globally with table level option capacity_mode. Valid values are :provisioned, :on_demand and nil:

table capacity_mode: :on_demand

If table capacity mode is on-demand, another related table-level options read_capacity and write_capacity will be ignored.

Fields

You'll have to define all the fields on the model and the data type of each field. Every field on the object must be included here; if you miss any they'll be completely bypassed during DynamoDB's initialization and will not appear on the model objects.

By default, fields are assumed to be of type string. Other built-in types are integer, number, set, array, map, datetime, date, boolean, binary, raw and serialized. array and map match List and Map DynamoDB types respectively. raw type means you can store Ruby Array, Hash, String and numbers. If built-in types do not suit you, you can use a custom field type represented by an arbitrary class, provided that the class supports a compatible serialization interface. The primary use case for using a custom field type is to represent your business logic with high-level types, while ensuring portability or backward-compatibility of the serialized representation.

Note on boolean type

The boolean fields are stored as DynamoDB boolean values by default. Dynamoid can store boolean values as strings as well - 't' and 'f'. So if you want to change the default format of boolean field you can easily achieve this with store_as_native_boolean field option:

class Document
  include Dynamoid::Document

  field :active, :boolean, store_as_native_boolean: false
end

Note on date type

By default date fields are persisted as days count since 1 January 1970 like UNIX time. If you prefer dates to be stored as ISO-8601 formatted strings instead then set store_as_string to true

class Document
  include Dynamoid::Document

  field :sent_on, :date, store_as_string: true
end

Note on datetime type

By default datetime fields are persisted as UNIX timestamps with millisecond precision in DynamoDB. If you prefer datetimes to be stored as ISO-8601 formatted strings instead then set store_as_string to true

class Document
  include Dynamoid::Document

  field :sent_at, :datetime, store_as_string: true
end

WARNING: Fields in numeric format are stored with nanoseconds as a fraction part and precision could be lost. That's why datetime field in numeric format shouldn't be used as a range key.

You have two options if you need to use a datetime field as a range key:

  • string format
  • store datetime values without milliseconds (e.g. cut them manually with change method - Time.now.change(usec: 0)

Note on set type

Dynamoid's type set is stored as DynamoDB's Set attribute type. DynamoDB supports only Set of strings, numbers and binary. Moreover Set must contain elements of the same type only.

In order to use some other Dynamoid's types you can specify of option to declare the type of set elements.

As a result of that DynamoDB limitation, in Dynamoid only the following scalar types are supported (note: does not support boolean): integer, number, date, datetime, serializable and custom types.

class Document
  include Dynamoid::Document

  field :tags, :set, of: :integer
end

It's possible to specify field options like store_as_string for datetime field or serializer for serializable field for set elements type:

class Document
  include Dynamoid::Document

  field :values, :set, of: { serialized: { serializer: JSON } }
  field :dates, :set, of: { date: { store_as_string: true } }
  field :datetimes, :set, of: { datetime: { store_as_string: false } }
end

DynamoDB doesn't allow empty strings in fields configured as set. Abiding by this restriction, when Dynamoid saves a document it removes all empty strings in set fields.

Note on array type

Dynamoid's type array is stored as DynamoDB's List attribute type. It can contain elements of different types (in contrast to Set attribute type).

If you need to store in array field elements of datetime, date, serializable or some custom type, which DynamoDB doesn't support natively, you should specify element type with of option:

class Document
  include Dynamoid::Document

  field :dates, :array, of: :date
end

Magic Columns

You get magic columns of id (string), created_at (datetime), and updated_at (datetime) for free.

class User
  include Dynamoid::Document

  field :name
  field :email
  field :rank, :integer
  field :number, :number
  field :joined_at, :datetime
  field :hash, :serialized
end

Default Values

You can optionally set a default value on a field using either a plain value or a lambda:

field :actions_taken, :integer, default: 0
field :joined_at, :datetime, default: -> { Time.now }

Aliases

It might be helpful to define an alias for already existing field when naming convention used for a table differs from conventions common in Ruby:

field firstName, :string, alias: :first_name

This way there will be generated setters/getters/<name>?/<name>_before_type_cast methods for both original field name (firstName) and an alias (first_name).

user = User.new(first_name: 'Michael')
user.first_name # => 'Michael'
user.firstName # => 'Michael'

Custom Types

To use a custom type for a field, suppose you have a Money type.

class Money
  # ... your business logic ...

  def dynamoid_dump
    'serialized representation as a string'
  end

  def self.dynamoid_load(_serialized_str)
    # parse serialized representation and return a Money instance
    Money.new(1.23)
  end
end

class User
  include Dynamoid::Document

  field :balance, Money
end

If you want to use a third-party class (which does not support #dynamoid_dump and .dynamoid_load) as your field type, you can use an adapter class providing .dynamoid_dump and .dynamoid_load class methods for your third-party class. .dynamoid_load can remain the same from the previous example; here we just add a level of indirection for serializing. Example:

# Third-party Money class
class Money; end

class MoneyAdapter
  def self.dynamoid_load(_money_serialized_str)
    Money.new(1.23)
  end

  def self.dynamoid_dump(money_obj)
    money_obj.value.to_s
  end
end

class User
  include Dynamoid::Document

  field :balance, MoneyAdapter
end

Lastly, you can control the data type of your custom-class-backed field at the DynamoDB level. This is especially important if you want to use your custom field as a numeric range or for number-oriented queries. By default custom fields are persisted as a string attribute, but your custom class can override this with a .dynamoid_field_type class method, which would return either :string or :number.

DynamoDB may support some other attribute types that are not yet supported by Dynamoid.

Sort key

Along with partition key table may have a sort key. In order to declare it in a model range class method should be used:

class Post
  include Dynamoid::Document

  range :posted_at, :datetime
end

Second argument, type, is optional. Default type is string.

Associations

Just like in ActiveRecord (or your other favorite ORM), Dynamoid uses associations to create links between models.

WARNING: Associations are not supported for models with compound primary key. If a model declares a range key it should not declare any association itself and be referenced by an association in another model.

The only supported associations (so far) are has_many, has_one, has_and_belongs_to_many, and belongs_to. Associations are very simple to create: just specify the type, the name, and then any options you'd like to pass to the association. If there's an inverse association either inferred or specified directly, Dynamoid will update both objects to point at each other.

class User
  include Dynamoid::Document

  # ...

  has_many :addresses
  has_many :students, class: User
  belongs_to :teacher, class_name: :user
  belongs_to :group
  belongs_to :group, foreign_key: :group_id
  has_one :role
  has_and_belongs_to_many :friends, inverse_of: :friending_users
end

class Address
  include Dynamoid::Document

  # ...

  belongs_to :user # Automatically links up with the user model
end

Contrary to what you'd expect, association information is always contained on the object specifying the association, even if it seems like the association has a foreign key. This is a side effect of DynamoDB's structure: it's very difficult to find foreign keys without an index. Usually you won't find this to be a problem, but it does mean that association methods that build new models will not work correctly - for example, user.addresses.new returns an address that is not associated to the user. We'll be correcting this soon maybe someday, if we get a pull request.

Validations

Dynamoid bakes in ActiveModel validations, just like ActiveRecord does.

class User
  include Dynamoid::Document

  # ...

  validates_presence_of :name
  validates_format_of :email, with: /@/
end

To see more usage and examples of ActiveModel validations, check out the ActiveModel validation documentation.

If you want to bypass model validation, pass validate: false to save call:

model.save(validate: false)

Callbacks

Dynamoid also employs ActiveModel callbacks. Right now the following callbacks are supported:

  • save (before, after, around)
  • create (before, after, around)
  • update (before, after, around)
  • validation (before, after)
  • destroy (before, after, around)
  • after_touch
  • after_initialize
  • after_find

Example:

class User
  include Dynamoid::Document

  # ...

  before_save :set_default_password
  after_create :notify_friends
  after_destroy :delete_addresses
end

STI

Dynamoid supports STI (Single Table Inheritance) like Active Record does. You need just specify type field in a base class. Example:

class Animal
  include Dynamoid::Document

  field :name
  field :type
end

class Cat < Animal
  field :lives, :integer
end

cat = Cat.create(name: 'Morgan')
animal = Animal.find(cat.id)
animal.class
#=> Cat

If you already have DynamoDB tables and type field already exists and has its own semantic it leads to conflict. It's possible to tell Dynamoid to use another field (even not existing) instead of type one with inheritance_field table option:

class Car
  include Dynamoid::Document
  table inheritance_field: :my_new_type

  field :my_new_type
end

c = Car.create
c.my_new_type
#=> "Car"

Type casting

Dynamoid supports type casting and tries to do it in the most convenient way. Values for all fields (except custom type) are coerced to declared field types.

Some obvious rules are used, e.g.:

for boolean field:

document.boolean_field = 'off'
# => false
document.boolean_field = 'false'
# => false
document.boolean_field = 'some string'
# => true

or for integer field:

document.integer_field = 42.3
# => 42
document.integer_field = '42.3'
# => 42
document.integer_field = true
# => 1

If time zone isn't specified for datetime value - application time zone is used.

To access field value before type casting following method could be used: attributes_before_type_cast and read_attribute_before_type_cast.

There is <name>_before_type_cast method for every field in a model as well.

Dirty API

Dynamoid supports Dirty API which is equivalent to Rails 5.2 ActiveModel::Dirty. There is only one limitation - change in place of field isn't detected automatically.

Usage

Object Creation

Dynamoid's syntax is generally very similar to ActiveRecord's. Making new objects is simple:

u = User.new(name: 'Josh')
u.email = '[email protected]'
u.save

Save forces persistence to the data store: a unique ID is also assigned, but it is a string and not an auto-incrementing number.

u.id # => '3a9f7216-4726-4aea-9fbc-8554ae9292cb'

To use associations, you use association methods very similar to ActiveRecord's:

address = u.addresses.create
address.city = 'Chicago'
address.save

To create multiple documents at once:

User.create([{ name: 'Josh' }, { name: 'Nick' }])

There is an efficient and low-level way to create multiple documents (without validation and callbacks running):

users = User.import([{ name: 'Josh' }, { name: 'Nick' }])

Querying

Querying can be done in one of three ways:

Address.find(address.id)              # Find directly by ID.
Address.where(city: 'Chicago').all    # Find by any number of matching criteria...
                                      # Though presently only "where" is supported.
Address.find_by_city('Chicago')       # The same as above, but using ActiveRecord's older syntax.

And you can also query on associations:

u.addresses.where(city: 'Chicago').all

But keep in mind Dynamoid - and document-based storage systems in general - are not drop-in replacements for existing relational databases. The above query does not efficiently perform a conditional join, but instead finds all the user's addresses and naively filters them in Ruby. For large associations this is a performance hit compared to relational database engines.

Warning: There is a caveat with filtering documents by nil value attribute. By default Dynamoid ignores attributes with nil value and doesn't store them in a DynamoDB document. This behavior could be changed with store_attribute_with_nil_value config option.

If Dynamoid ignores nil value attributes null/not_null operators should be used in query:

Address.where('postcode.null': true)
Address.where('postcode.not_null': true)

If Dynamoid keeps nil value attributes eq/ne operators should be used instead:

Address.where(postcode: nil)
Address.where('postcode.ne': nil)

Limits

There are three types of limits that you can query with:

  1. record_limit - The number of evaluated records that are returned by the query.
  2. scan_limit - The number of scanned records that DynamoDB will look at before returning.
  3. batch_size - The number of records requested to DynamoDB per underlying request, good for large queries!

Using these in various combinations results in the underlying requests to be made in the smallest size possible and the query returns once record_limit or scan_limit is satisfied. It will attempt to batch whenever possible.

You can thus limit the number of evaluated records, or select a record from which to start in order to support pagination.

Address.record_limit(5).start(address) # Only 5 addresses starting at `address`

Where address is an instance of the model or a hash {the_model_hash_key: 'value', the_model_range_key: 'value'}. Keep in mind that if you are passing a hash to .start() you need to explicitly define all required keys in it including range keys, depending on table or secondary indexes signatures, otherwise you'll get an Aws::DynamoDB::Errors::ValidationException either for Exclusive Start Key must have same size as table's key schema or The provided starting key is invalid

If you are potentially running over a large data set and this is especially true when using certain filters, you may want to consider limiting the number of scanned records (the number of records DynamoDB infrastructure looks through when evaluating data to return):

Address.scan_limit(5).start(address) # Only scan at most 5 records and return what's found starting from `address`

For large queries that return many rows, Dynamoid can use AWS' support for requesting documents in batches:

# Do some maintenance on the entire table without flooding DynamoDB
Address.batch(100).each { |addr| addr.do_some_work && sleep(0.01) }
Address.record_limit(10_000).batch(100).each { |addr| addr.do_some_work && sleep(0.01) } # Batch specified as part of a chain

The implication of batches is that the underlying requests are done in the batch sizes to make the request and responses more manageable. Note that this batching is for Query and Scans and not BatchGetItem commands.

DynamoDB pagination

At times it can be useful to rely on DynamoDB low-level pagination instead of fixed pages sizes. Each page results in a single Query or Scan call to DynamoDB, but returns an unknown number of records.

Access to the native DynamoDB pages can be obtained via the find_by_pages method, which yields arrays of records.

Address.find_by_pages do |addresses, metadata|
end

Each yielded pages returns page metadata as the second argument, which is a hash including a key :last_evaluated_key. The value of this key can be used for the start method to fetch the next page of records.

This way it can be used for instance to implement efficiently pagination in web-applications:

class UserController < ApplicationController
  def index
    next_page = params[:next_page_token] ? JSON.parse(Base64.decode64(params[:next_page_token])) : nil

    records, metadata = User.start(next_page).find_by_pages.first

    render json: {
      records: records,
      next_page_token: Base64.encode64(metadata[:last_evaluated_key].to_json)
    }
  end
end

Sort Conditions and Filters

You are able to optimize query with condition for sort key. Following operators are available: gt, lt, gte, lte, begins_with, between as well as equality:

Address.where(latitude: 10_212)
Address.where('latitude.gt': 10_212)
Address.where('latitude.lt': 10_212)
Address.where('latitude.gte': 10_212)
Address.where('latitude.lte': 10_212)
Address.where('city.begins_with': 'Lon')
Address.where('latitude.between': [10_212, 20_000])

You are able to filter results on the DynamoDB side and specify conditions for non-key fields. Following additional operators are available: in, contains, not_contains, null, not_null:

Address.where('city.in': %w[London Edenburg Birmingham])
Address.where('city.contains': ['on'])
Address.where('city.not_contains': ['ing'])
Address.where('postcode.null': false)
Address.where('postcode.not_null': true)

WARNING: Please take into account that NULL and NOT_NULL operators check attribute presence in a document, not value. So if attribute postcode's value is NULL, NULL operator will return false because attribute exists even if has NULL value.

Selecting some specific fields only

It could be done with project method:

class User
  include Dynamoid::Document
  field :name
end

User.create(name: 'Alex')
user = User.project(:name).first

user.id         # => nil
user.name       # => 'Alex'
user.created_at # => nil

Returned models with have filled specified fields only.

Several fields could be specified:

user = User.project(:name, :created_at)

Consistent Reads

Querying supports consistent reading. By default, DynamoDB reads are eventually consistent: if you do a write and then a read immediately afterwards, the results of the previous write may not be reflected. If you need to do a consistent read (that is, you need to read the results of a write immediately) you can do so, but keep in mind that consistent reads are twice as expensive as regular reads for DynamoDB.

Address.find(address.id, consistent_read: true)  # Find an address, ensure the read is consistent.
Address.where(city: 'Chicago').consistent.all    # Find all addresses where the city is Chicago, with a consistent read.

Range Finding

If you have a range index, Dynamoid provides a number of additional other convenience methods to make your life a little easier:

User.where('created_at.gt': DateTime.now - 1.day).all
User.where('created_at.lt': DateTime.now - 1.day).all

It also supports gte and lte. Turning those into symbols and allowing a Rails SQL-style string syntax is in the works. You can only have one range argument per query, because of DynamoDB inherent limitations, so use it sensibly!

Updating

In order to update document you can use high level methods #update_attributes, #update_attribute and .update. They run validation and callbacks.

Address.find(id).update_attributes(city: 'Chicago')
Address.find(id).update_attribute(:city, 'Chicago')
Address.update(id, city: 'Chicago')

There are also some low level methods #update, .update_fields and .upsert. They don't run validation and callbacks (except #update - it runs update callbacks). All of them support conditional updates. #upsert will create new document if document with specified id doesn't exist.

Address.find(id).update do |i|
  i.set city: 'Chicago'
  i.add latitude: 100
  i.delete set_of_numbers: 10
end
Address.find(id).update(if: { deliverable: true }) do |i|
  i.set city: 'Chicago'
end
Address.update_fields(id, city: 'Chicago')
Address.update_fields(id, { city: 'Chicago' }, if: { deliverable: true })
Address.upsert(id, city: 'Chicago')
Address.upsert(id, { city: 'Chicago' }, if: { deliverable: true })

By default, #upsert will update all attributes of the document if it already exists. To idempotently create-but-not-update a record, apply the unless_exists condition to its keys when you upsert.

Address.upsert(id, { city: 'Chicago' }, { unless_exists: [:id] })

Deleting

In order to delete some items delete_all method should be used. Any callback won't be called. Items delete in efficient way in batch.

Address.where(city: 'London').delete_all

Global Secondary Indexes

You can define index with global_secondary_index:

class User
  include Dynamoid::Document

  field :name
  field :age, :number

  global_secondary_index hash_key: :age # Must come after field definitions.
end

There are the following options:

  • hash_key - is used as hash key of an index,
  • range_key - is used as range key of an index,
  • projected_attributes - list of fields to store in an index or has a predefined value :keys_only, :all; :keys_only is a default,
  • name - an index will be created with this name when a table is created; by default name is generated and contains table name and keys names,
  • read_capacity - is used when table created and used as an index capacity; by default equals Dynamoid::Config.read_capacity,
  • write_capacity - is used when table created and used as an index capacity; by default equals Dynamoid::Config.write_capacity

The only mandatory option is name.

WARNING: In order to use global secondary index in Document.where implicitly you need to have all the attributes of the original table in the index and declare it with option projected_attributes: :all:

class User
  # ...

  global_secondary_index hash_key: :age, projected_attributes: :all
end

There is only one implicit way to query Global and Local Secondary Indexes (GSI/LSI).

Implicit

The second way implicitly uses your GSI through the where clauses and deduces the index based on the query fields provided. Another added benefit is that it is built into query chaining so you can use all the methods used in normal querying. The explicit way from above would be rewritten as follows:

where(dynamo_primary_key_column_name => dynamo_primary_key_value,
      "#{range_column}.#{range_modifier}" => range_value)
  .scan_index_forward(false)

The only caveat with this method is that because it is also used for general querying, it WILL NOT use a GSI unless it explicitly has defined projected_attributes: :all on the GSI in your model. This is because GSIs that do not have all attributes projected will only contain the index keys and therefore will not return objects with fully resolved field values. It currently opts to provide the complete results rather than partial results unless you've explicitly looked up the data.

Future TODO could involve implementing select in chaining as well as resolving the fields with a second query against the table since a query against GSI then a query on base table is still likely faster than scan on the base table

Transaction Writes

Multiple write actions can be grouped together and submitted as an all-or-nothing operation. See the transation documentation.

PartiQL

To run PartiQL statements Dynamoid.adapter.execute method should be used:

Dynamoid.adapter.execute("UPDATE users SET name = 'Mike' WHERE id = '1'")

Parameters are also supported:

Dynamoid.adapter.execute('SELECT * FROM users WHERE id = ?', ['1'])

Configuration

Listed below are all configuration options.

  • adapter - useful only for the gem developers to switch to a new adapter. Default and the only available value is aws_sdk_v3
  • namespace - prefix for table names, default is dynamoid_#{application_name}_#{environment} for Rails application and dynamoid otherwise
  • logger - by default it's a Rails.logger in Rails application and stdout otherwise. You can disable logging by setting nil or false values. Set true value to use defaults
  • access_key - DynamoDb custom access key for AWS credentials, override global AWS credentials if they're present
  • secret_key - DynamoDb custom secret key for AWS credentials, override global AWS credentials if they're present
  • credentials - DynamoDb custom pre-configured credentials, override global AWS credentials if they're present
  • region - DynamoDb custom credentials for AWS, override global AWS credentials if they're present
  • batch_size - when you try to load multiple items at once with
  • batch_get_item call Dynamoid loads them not with one api call but piece by piece. Default is 100 items
  • capacity_mode - used at a table creation and means whether a table read/write capacity mode will be on-demand or provisioned. Allowed values are :on_demand and :provisioned. Default value is nil which means provisioned mode will be used.
  • read_capacity - is used at table or indices creation. Default is 100 (units)
  • write_capacity - is used at table or indices creation. Default is 20 (units)
  • warn_on_scan - log warnings when scan table. Default is true
  • endpoint - if provided, it communicates with the DynamoDB listening at the endpoint. This is useful for testing with DynamoDB Local
  • identity_map - ensures that each object gets loaded only once by keeping every loaded object in a map. Looks up objects using the map when referring to them. Isn't thread safe. Default is false. Use Dynamoid::Middleware::IdentityMap to clear identity map for each HTTP request
  • timestamps - by default Dynamoid sets created_at and updated_at fields at model creation and updating. You can disable this behavior by setting false value
  • sync_retry_max_times - when Dynamoid creates or deletes table synchronously it checks for completion specified times. Default is 60 (times). It's a bit over 2 minutes by default
  • sync_retry_wait_seconds - time to wait between retries. Default is 2 (seconds)
  • convert_big_decimal - if true then Dynamoid converts numbers stored in Hash in raw field to float. Default is false
  • store_attribute_with_nil_value - if true Dynamoid keeps attribute with nil value in a document. Otherwise Dynamoid removes it while saving a document. Default is nil which equals behaviour with false value.
  • models_dir - dynamoid:create_tables rake task loads DynamoDb models from this directory. Default is ./app/models.
  • application_timezone - Dynamoid converts all datetime fields to specified time zone when loads data from the storage. Acceptable values - :utc, :local (to use system time zone) and time zone name e.g. Eastern Time (US & Canada). Default is utc
  • dynamodb_timezone - When a datetime field is stored in string format Dynamoid converts it to specified time zone when saves a value to the storage. Acceptable values - :utc, :local (to use system time zone) and time zone name e.g. Eastern Time (US & Canada). Default is utc
  • store_datetime_as_string - if true then Dynamoid stores :datetime fields in ISO 8601 string format. Default is false
  • store_date_as_string - if true then Dynamoid stores :date fields in ISO 8601 string format. Default is false
  • store_boolean_as_native - if true Dynamoid stores boolean fields as native DynamoDB boolean values. Otherwise boolean fields are stored as string values 't' and 'f'. Default is true
  • backoff - is a hash: key is a backoff strategy (symbol), value is parameters for the strategy. Is used in batch operations. Default id nil
  • backoff_strategies: is a hash and contains all available strategies. Default is { constant: ..., exponential: ...}
  • log_formatter: overrides default AWS SDK formatter. There are several canned formatters: Aws::Log::Formatter.default, Aws::Log::Formatter.colored and Aws::Log::Formatter.short. Please look into Aws::Log::Formatter AWS SDK documentation in order to provide own formatter.
  • http_continue_timeout: The number of seconds to wait for a 100-continue HTTP response before sending the request body. Default option value is nil. If not specified effected value is 1
  • http_idle_timeout: The number of seconds an HTTP connection is allowed to sit idle before it is considered stale. Default option value is nil. If not specified effected value is 5
  • http_open_timeout: The number of seconds to wait when opening a HTTP session. Default option value is nil. If not specified effected value is 15
  • http_read_timeout:The number of seconds to wait for HTTP response data. Default option value is nil. If not specified effected value is 60
  • create_table_on_save: if true then Dynamoid creates a corresponding table in DynamoDB at model persisting if the table doesn't exist yet. Default is true

Concurrency

Dynamoid supports basic, ActiveRecord-like optimistic locking on save operations. Simply add a lock_version column to your table like so:

class MyTable
  # ...

  field :lock_version, :integer

  # ...
end

In this example, all saves to MyTable will raise an Dynamoid::Errors::StaleObjectError if a concurrent process loaded, edited, and saved the same row. Your code should trap this exception, reload the row (so that it will pick up the newest values), and try the save again.

Calls to update and update! also increment the lock_version, however, they do not check the existing value. This guarantees that a update operation will raise an exception in a concurrent save operation, however a save operation will never cause an update to fail. Thus, update is useful & safe only for doing atomic operations (e.g. increment a value, add/remove from a set, etc), but should not be used in a read-modify-write pattern.

Backoff strategies

You can use several methods that run efficiently in batch mode like .find_all and .import. It affects Query and Scan operations as well.

The backoff strategy will be used when, for any reason, some items could not be processed as part of a batch mode command. Operations will be re-run to process these items.

Exponential backoff is the recommended way to handle throughput limits exceeding and throttling on the table.

There are two built-in strategies - constant delay and truncated binary exponential backoff. By default no backoff is used but you can specify one of the built-in ones:

Dynamoid.configure do |config|
  config.backoff = { constant: 2.second }
end

Dynamoid.configure do |config|
  config.backoff = { exponential: { base_backoff: 0.2.seconds, ceiling: 10 } }
end

You can just specify strategy without any arguments to use default presets:

Dynamoid.configure do |config|
  config.backoff = :constant
end

You can use your own strategy in the following way:

Dynamoid.configure do |config|
  config.backoff_strategies[:custom] = lambda do |n|
    -> { sleep rand(n) }
  end

  config.backoff = { custom: 10 }
end

Rake Tasks

There are a few Rake tasks available out of the box:

  • rake dynamoid:create_tables
  • rake dynamoid:ping

In order to use them in non-Rails application they should be required explicitly:

# Rakefile

Rake::Task.define_task(:environment)
require 'dynamoid/tasks'

The Rake tasks depend on :environment task so it should be declared as well.

Test Environment

In test environment you will most likely want to clean the database between test runs to keep tests completely isolated. This can be achieved like so

module DynamoidReset
  def self.all
    Dynamoid.adapter.list_tables.each do |table|
      # Only delete tables in our namespace
      if table =~ /^#{Dynamoid::Config.namespace}/
        Dynamoid.adapter.delete_table(table)
      end
    end
    Dynamoid.adapter.tables.clear
    # Recreate all tables to avoid unexpected errors
    Dynamoid.included_models.each { |m| m.create_table(sync: true) }
  end
end

# Reduce noise in test output
Dynamoid.logger.level = Logger::FATAL

If you're using RSpec you can invoke the above like so:

RSpec.configure do |config|
  config.before(:each) do
    DynamoidReset.all
  end
end

In addition, the first test for each model may fail if the relevant models are not included in included_models. This can be fixed by adding this line before the DynamoidReset module:

Dir[File.join(Dynamoid::Config.models_dir, '**/*.rb')].sort.each { |file| require file }

Note that this will require all models in your models folder - you can also explicitly require only certain models if you would prefer to.

In Rails, you may also want to ensure you do not delete non-test data accidentally by adding the following to your test environment setup:

raise "Tests should be run in 'test' environment only" if Rails.env != 'test'

Dynamoid.configure do |config|
  config.namespace = "#{Rails.application.railtie_name}_#{Rails.env}"
end

Logging

There is a config option logger. Dynamoid writes requests and responses to DynamoDB using this logger on the debug level. So in order to troubleshoot and debug issues just set it:

class User
  include Dynamoid::Document
  field name
end

Dynamoid.config.logger.level = :debug
Dynamoid.config.endpoint = 'http://localhost:8000'

User.create(name: 'Alex')

# => D, [2019-05-12T20:01:07.840051 #75059] DEBUG -- : put_item | Request "{\"TableName\":\"dynamoid_users\",\"Item\":{\"created_at\":{\"N\":\"1557680467.608749\"},\"updated_at\":{\"N\":\"1557680467.608809\"},\"id\":{\"S\":\"1227eea7-2c96-4b8a-90d9-77b38eb85cd0\"}},\"Expected\":{\"id\":{\"Exists\":false}}}" | Response "{}"

# => D, [2019-05-12T20:01:07.842397 #75059] DEBUG -- : (231.28 ms) PUT ITEM - ["dynamoid_users", {:created_at=>0.1557680467608749e10, :updated_at=>0.1557680467608809e10, :id=>"1227eea7-2c96-4b8a-90d9-77b38eb85cd0", :User=>nil}, {}]

The first line is a body of HTTP request and response. The second line - Dynamoid internal logging of API call (PUT ITEM in our case) with timing (231.28 ms).

Credits

Dynamoid borrows code, structure, and even its name very liberally from the truly amazing Mongoid. Without Mongoid to crib from none of this would have been possible, and I hope they don't mind me reusing their very awesome ideas to make DynamoDB just as accessible to the Ruby world as MongoDB.

Also, without contributors the project wouldn't be nearly as awesome. So many thanks to:

* Current Maintainers

Running the tests

Running the tests is fairly simple. You should have an instance of DynamoDB running locally. Follow these steps to setup your test environment.

  • First download and unpack the latest version of DynamoDB. We have a script that will do this for you if you use bash, and homebrew on a Mac.

    bin/setup
  • Start the local instance of DynamoDB to listen in 8000 port

    bin/start_dynamodblocal
  • and lastly, use rake to run the tests.

    rake
  • When you are done, remember to stop the local test instance of dynamodb

    bin/stop_dynamodblocal

If you run into issues, please try these steps first. NOTE: You can use any version manager: rvm, rbenv, chruby, asdf-ruby

asdf install ruby 3.1.1
asdf local ruby 3.1.1
gem update --system
bundle install

Security

See SECURITY.md.

Related links

This documentation may be useful for the contributors:

License

The gem is available as open source under the terms of the MIT License License: MIT. See LICENSE for the official Copyright Notice.

dynamoid's People

Contributors

a5-stable avatar aaronmallen avatar afrojas avatar alexnisnevich avatar alexperto avatar ananthakumaran avatar andrykonchin avatar ayemos avatar bmalinconico avatar bonty avatar brenden avatar cheneveld avatar cignoir avatar dependabot[bot] avatar jasondew avatar loganb avatar lucasas avatar mpatric avatar mrkamel avatar pboling avatar pcorpet avatar peeyush1234 avatar philipmw avatar rahul342 avatar richardhsu avatar satoruk avatar stefanneculai avatar sumocoder avatar veraticus avatar xeger avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dynamoid's Issues

Error while migrating from dynamoid 0.6 to 1.1

Hi,

I am getting the below error while querying the dynamodb database.

expected hash value for member 1 of key attribute_value_list of value at key is_submitted of option scan_filter

I have recently migrated from dynamoid 0.6 to 1.1.

I am getting this error on line.

Node.where(:is_submitted => 't').where(:is_signed_off => 'f').count

Node is the model in rails application.

Below is the stack trace :

aws-sdk (1.20.0) lib/aws/core/option_grammar.rb:337:in validate' aws-sdk (1.20.0) lib/aws/core/option_grammar.rb:245:inblock in validate'
aws-sdk (1.20.0) lib/aws/core/option_grammar.rb:243:in each' aws-sdk (1.20.0) lib/aws/core/option_grammar.rb:243:invalidate'
aws-sdk (1.20.0) lib/aws/core/option_grammar.rb:346:in block in validate' aws-sdk (1.20.0) lib/aws/core/option_grammar.rb:342:ineach'
aws-sdk (1.20.0) lib/aws/core/option_grammar.rb:342:in validate' aws-sdk (1.20.0) lib/aws/core/option_grammar.rb:410:inblock in validate'
aws-sdk (1.20.0) lib/aws/core/option_grammar.rb:408:in each' aws-sdk (1.20.0) lib/aws/core/option_grammar.rb:408:invalidate'
aws-sdk (1.20.0) lib/aws/core/option_grammar.rb:590:in block in validate' aws-sdk (1.20.0) lib/aws/core/option_grammar.rb:586:ineach'
aws-sdk (1.20.0) lib/aws/core/option_grammar.rb:586:in validate' aws-sdk (1.20.0) lib/aws/core/option_grammar.rb:611:into_h'
aws-sdk (1.20.0) lib/aws/core/option_grammar.rb:621:in to_json' aws-sdk (1.20.0) lib/aws/core/json_request_builder.rb:29:inpopulate_request'
aws-sdk (1.20.0) lib/aws/core/client.rb:704:in block (2 levels) in define_client_method' aws-sdk (1.20.0) lib/aws/core/client.rb:549:inbuild_request'
aws-sdk (1.20.0) lib/aws/core/client.rb:482:in block (3 levels) in client_request' aws-sdk (1.20.0) lib/aws/core/response.rb:171:incall'
aws-sdk (1.20.0) lib/aws/core/response.rb:171:in build_request' aws-sdk (1.20.0) lib/aws/core/response.rb:111:ininitialize'
aws-sdk (1.20.0) lib/aws/core/client.rb:195:in new' aws-sdk (1.20.0) lib/aws/core/client.rb:195:innew_response'
aws-sdk (1.20.0) lib/aws/core/client.rb:481:in block (2 levels) in client_request' aws-sdk (1.20.0) lib/aws/core/client.rb:382:inlog_client_request'
aws-sdk (1.20.0) lib/aws/core/client.rb:468:in block in client_request' aws-sdk (1.20.0) lib/aws/core/client.rb:364:inreturn_or_raise'
aws-sdk (1.20.0) lib/aws/core/client.rb:467:in client_request' (eval):3:inscan'
dynamoid (1.1.0) lib/dynamoid/adapter_plugin/aws_sdk_v2.rb:368:in block (2 levels) in scan' dynamoid (1.1.0) lib/dynamoid/adapter_plugin/aws_sdk_v2.rb:367:inloop'
dynamoid (1.1.0) lib/dynamoid/adapter_plugin/aws_sdk_v2.rb:367:in block in scan' dynamoid (1.1.0) lib/dynamoid/criteria/chain.rb:142:ineach'
dynamoid (1.1.0) lib/dynamoid/criteria/chain.rb:142:in each' dynamoid (1.1.0) lib/dynamoid/criteria/chain.rb:142:inblock in records_via_scan'
dynamoid (1.1.0) lib/dynamoid/criteria/chain.rb:115:in each' dynamoid (1.1.0) lib/dynamoid/criteria/chain.rb:115:ineach'
dynamoid (1.1.0) lib/dynamoid/criteria/chain.rb:115:in to_a' dynamoid (1.1.0) lib/dynamoid/criteria/chain.rb:115:inArray'
dynamoid (1.1.0) lib/dynamoid/criteria/chain.rb:115:in records' dynamoid (1.1.0) lib/dynamoid/criteria/chain.rb:95:ineach'
app/controllers/application_controller.rb:106:in count' app/controllers/application_controller.rb:106:inset_global_counts'
activesupport (4.1.14.2) lib/active_support/callbacks.rb:429:in block in make_lambda' activesupport (4.1.14.2) lib/active_support/callbacks.rb:143:incall'
activesupport (4.1.14.2) lib/active_support/callbacks.rb:143:in block in halting_and_conditional' activesupport (4.1.14.2) lib/active_support/callbacks.rb:501:incall'
activesupport (4.1.14.2) lib/active_support/callbacks.rb:501:in block in call' activesupport (4.1.14.2) lib/active_support/callbacks.rb:501:ineach'
activesupport (4.1.14.2) lib/active_support/callbacks.rb:501:in call' activesupport (4.1.14.2) lib/active_support/callbacks.rb:86:inrun_callbacks'
actionpack (4.1.14.2) lib/abstract_controller/callbacks.rb:19:in process_action' actionpack (4.1.14.2) lib/action_controller/metal/rescue.rb:29:inprocess_action'
actionpack (4.1.14.2) lib/action_controller/metal/instrumentation.rb:32:in block in process_action' activesupport (4.1.14.2) lib/active_support/notifications.rb:159:inblock in instrument'
activesupport (4.1.14.2) lib/active_support/notifications/instrumenter.rb:20:in instrument' activesupport (4.1.14.2) lib/active_support/notifications.rb:159:ininstrument'
actionpack (4.1.14.2) lib/action_controller/metal/instrumentation.rb:30:in process_action' actionpack (4.1.14.2) lib/action_controller/metal/params_wrapper.rb:250:inprocess_action'
actionpack (4.1.14.2) lib/abstract_controller/base.rb:136:in process' actionview (4.1.14.2) lib/action_view/rendering.rb:30:inprocess'
actionpack (4.1.14.2) lib/action_controller/metal.rb:196:in dispatch' actionpack (4.1.14.2) lib/action_controller/metal/rack_delegation.rb:13:indispatch'
actionpack (4.1.14.2) lib/action_controller/metal.rb:232:in block in action' actionpack (4.1.14.2) lib/action_dispatch/routing/route_set.rb:80:incall'
actionpack (4.1.14.2) lib/action_dispatch/routing/route_set.rb:80:in dispatch' actionpack (4.1.14.2) lib/action_dispatch/routing/route_set.rb:48:incall'
actionpack (4.1.14.2) lib/action_dispatch/journey/router.rb:73:in block in call' actionpack (4.1.14.2) lib/action_dispatch/journey/router.rb:59:ineach'
actionpack (4.1.14.2) lib/action_dispatch/journey/router.rb:59:in call' actionpack (4.1.14.2) lib/action_dispatch/routing/route_set.rb:690:incall'
rack (1.5.2) lib/rack/etag.rb:23:in call' rack (1.5.2) lib/rack/conditionalget.rb:25:incall'
rack (1.5.2) lib/rack/head.rb:11:in call' actionpack (4.1.14.2) lib/action_dispatch/middleware/params_parser.rb:27:incall'
actionpack (4.1.14.2) lib/action_dispatch/middleware/flash.rb:254:in call' rack (1.5.2) lib/rack/session/abstract/id.rb:225:incontext'
rack (1.5.2) lib/rack/session/abstract/id.rb:220:in call' actionpack (4.1.14.2) lib/action_dispatch/middleware/cookies.rb:562:incall'
actionpack (4.1.14.2) lib/action_dispatch/middleware/callbacks.rb:29:in block in call' activesupport (4.1.14.2) lib/active_support/callbacks.rb:82:inrun_callbacks'
actionpack (4.1.14.2) lib/action_dispatch/middleware/callbacks.rb:27:in call' actionpack (4.1.14.2) lib/action_dispatch/middleware/reloader.rb:73:incall'
actionpack (4.1.14.2) lib/action_dispatch/middleware/remote_ip.rb:76:in call' actionpack (4.1.14.2) lib/action_dispatch/middleware/debug_exceptions.rb:17:incall'
actionpack (4.1.14.2) lib/action_dispatch/middleware/show_exceptions.rb:30:in call' railties (4.1.14.2) lib/rails/rack/logger.rb:38:incall_app'
railties (4.1.14.2) lib/rails/rack/logger.rb:20:in block in call' activesupport (4.1.14.2) lib/active_support/tagged_logging.rb:68:inblock in tagged'
activesupport (4.1.14.2) lib/active_support/tagged_logging.rb:26:in tagged' activesupport (4.1.14.2) lib/active_support/tagged_logging.rb:68:intagged'
railties (4.1.14.2) lib/rails/rack/logger.rb:20:in call' /opt/brazil-pkg-cache/packages/AmazonRailsLogger/AmazonRailsLogger-4.0.80.53/RHEL5_64/DEV.STD.PTHREAD/build/ruby1.9/site_ruby/1.9.1/amazon/set_request_id.rb:9:incall'
/opt/brazil-pkg-cache/packages/AmazonRailsLogger/AmazonRailsLogger-4.0.80.53/RHEL5_64/DEV.STD.PTHREAD/build/ruby1.9/site_ruby/1.9.1/amazon/set_request_id.rb:9:in call' actionpack (4.1.14.2) lib/action_dispatch/middleware/request_id.rb:21:incall'
rack (1.5.2) lib/rack/methodoverride.rb:21:in call' rack (1.5.2) lib/rack/runtime.rb:17:incall'
activesupport (4.1.14.2) lib/active_support/cache/strategy/local_cache_middleware.rb:26:in call' rack (1.5.2) lib/rack/lock.rb:17:incall'
actionpack (4.1.14.2) lib/action_dispatch/middleware/static.rb:84:in call' rack (1.5.2) lib/rack/sendfile.rb:112:incall'
railties (4.1.14.2) lib/rails/engine.rb:514:in call' railties (4.1.14.2) lib/rails/application.rb:144:incall'
railties (4.1.14.2) lib/rails/railtie.rb:194:in public_send' railties (4.1.14.2) lib/rails/railtie.rb:194:inmethod_missing'
rack (1.5.2) lib/rack/lint.rb:49:in _call' rack (1.5.2) lib/rack/lint.rb:37:incall'
rack (1.5.2) lib/rack/showexceptions.rb:24:in call' rack (1.5.2) lib/rack/commonlogger.rb:33:incall'
rack (1.5.2) lib/rack/chunked.rb:43:in call' rack (1.5.2) lib/rack/content_length.rb:14:incall'
rack (1.5.2) lib/rack/handler/webrick.rb:60:in service' /home/lakhwani/FCLaunchRequirement/env/FCLaunchRequirementWebsite-1.0/runtime/ruby1.9/1.9.1/webrick/httpserver.rb:138:inservice'
/home/lakhwani/FCLaunchRequirement/env/FCLaunchRequirementWebsite-1.0/runtime/ruby1.9/1.9.1/webrick/httpserver.rb:94:in run' /home/lakhwani/FCLaunchRequirement/env/FCLaunchRequirementWebsite-1.0/runtime/ruby1.9/1.9.1/webrick/server.rb:191:inblock in start_thread'

Can anyone help me in this?

Provide option to skip namespace in configuration

I've been rewriting a legacy app in Ruby. When I try to provide a custom name to my table name Dynamoid prepends namespace to the table name. It would be great if we can skip namespace in configuration. I also tried leaving the namespace blank which results in _ before the table name.

Rails 5 support + ActiveRecord associations?

Hi, I'm a new developer so I'm not sure if this is the best place to ask these questions, let me know if I should move this somewhere else. I'm running a small project in Rails 5 and would like to use Dynamoid, and I saw that support was in the works. Is there any estimate on when that version will be released?

Also, when I assign associations in Dynamoid, is there any built in way to assign ddb documents to ActiveRecord objects? For example, if I am using dynamo to build a new feature for user's posts (comments, photos, videos, etc.) is there a way for me to assign each document in a way where I can call @user.posts and receive an array of Dynamoid objects with foreign keys of user_ids? Is it as simple as defining belongs_to and has_many in the models respectively, even if Users are stored in mysql?

Range finding functionality ('field.gt' et al) works only for Query, not Scan

AWS SDK v2 supports an optional scan_filter on Scan API calls, with the same semantics as query_filter on Query API calls.

Dynamoid offers convenience methods for finding on range fields, such as my_chain.where("rangefield.gt" => 5), but it works only on Query API calls. On Scan, Dynamoid allows this query chain, but it does not translate the ".gt" component, so you end up scanning for a non-existent field.

We need the range finding functionality to be consistent between Query and Scan.

Save fails if Dynamoid has to create a table

If the table does not yet exist, a call to model.save fails on first call and succeeds on subsequent calls.

irb(main):005:0> r.save
(456.44 ms) LIST TABLES - [[]]
(456.76 ms) CACHE TABLES
Creating olev_residentialapplications table. This could take a while.
(63.21 ms) CREATE TABLE
Aws::DynamoDB::Errors::ResourceNotFoundException: Requested resource not found
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/aws-sdk-core-2.3.1/lib/seahorse/client/plugins/raise_response_errors.rb:15:in `call'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/aws-sdk-core-2.3.1/lib/aws-sdk-core/plugins/dynamodb_simple_attributes.rb:112:in `call'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/aws-sdk-core-2.3.1/lib/aws-sdk-core/plugins/param_converter.rb:20:in `call'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/aws-sdk-core-2.3.1/lib/seahorse/client/plugins/response_target.rb:21:in `call'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/aws-sdk-core-2.3.1/lib/seahorse/client/request.rb:70:in `send_request'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/aws-sdk-core-2.3.1/lib/seahorse/client/base.rb:207:in `block (2 levels) in define_operation_methods'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dynamoid-1.1.0/lib/dynamoid/adapter_plugin/aws_sdk_v2.rb:249:in `put_item'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dynamoid-1.1.0/lib/dynamoid/adapter.rb:139:in `block (3 levels) in <class:Adapter>'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dynamoid-1.1.0/lib/dynamoid/adapter.rb:53:in `benchmark'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dynamoid-1.1.0/lib/dynamoid/adapter.rb:139:in `block (2 levels) in <class:Adapter>'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dynamoid-1.1.0/lib/dynamoid/adapter.rb:68:in `write'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dynamoid-1.1.0/lib/dynamoid/persistence.rb:308:in `block in persist'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/callbacks.rb:117:in `call'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/callbacks.rb:555:in `block (2 levels) in compile'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/callbacks.rb:505:in `call'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/callbacks.rb:92:in `__run_callbacks__'
... 8 levels...
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/callbacks.rb:778:in `_run_create_callbacks'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/callbacks.rb:81:in `run_callbacks'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dynamoid-1.1.0/lib/dynamoid/persistence.rb:165:in `save'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dynamoid-1.1.0/lib/dynamoid/validations.rb:17:in `save'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dynamoid-1.1.0/lib/dynamoid/identity_map.rb:68:in `save'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dynamoid-1.1.0/lib/dynamoid/dirty.rb:13:in `block in save'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dynamoid-1.1.0/lib/dynamoid/dirty.rb:28:in `clear_changes'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dynamoid-1.1.0/lib/dynamoid/dirty.rb:13:in `save'
    from (irb):5
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-4.2.6/lib/rails/commands/console.rb:110:in `start'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-4.2.6/lib/rails/commands/console.rb:9:in `start'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-4.2.6/lib/rails/commands/commands_tasks.rb:68:in `console'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-4.2.6/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
    from /Users/nbt/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-4.2.6/lib/rails/commands.rb:17:in `<top (required)>'
    from bin/rails:4:in `require'
    from bin/rails:4:in `<main>'irb(main):006:0> r.save
(595.87 ms) PUT ITEM - [["olev_residentialapplications", {:created_at=>1462523745.554208, :updated_at=>1462523745.554333, :id=>"d703d3fa-dc9b-445d-9b91-fd09a7fec770", :customer_name=>"aaa", :customer_adress=>"111"}, {:unless_exists=>[:id, :id]}]]

Timestamp fields may not compare equal after round-trip to DynamoDB

Timestamp fields may not compare equal after round-trip to DynamoDB. The root cause is that Time.at(some_time_obj.to_f) is not always equal to some_time_obj, in at least MRI and JRuby.

For now, the best workaround is to compare timestamps with the aid of an epsilon.

Another solution is for Dynamoid to use Time#_dump and Time::_load to persist timestamps, but we must take care to not break backward compatibility with existing records in DynamoDB.

Setting nil to an attribute that has a default does not work on initialize

class User
  include Dynamoid::Document

  field :name, :string,  {:default => "test"}
end

u = User.new(:name => nil)
# name value is set to "test" instead of nil here
u.name # => "test"

u = User.new
# default here is fine
u.name # => "test"

Default should only be set if the attribute was not set on initialize

Dynamoid::Criteria::Chain uses Enumerable#first for find by resulting in scan even if index is present

We discovered this quite unpleasantly via accidentally hitting Dynamoid::Finders#method_missing when a refactor failed to configure some custom find_by methods under certain circumstances not caught by our test suite, but it seems like we can probably refactor this to use indexes when the find by is on an indexed attribute, if we define Dynamoid::Criteria::Chain#first to reflect on indexes and do find_all_by_secondary_index(chain.query).first or something to that effect. Possibly we can also add a config option to error instead of falling back on Enumerable#first if the indexes don't exist, as that may be preferable to doing a full table scan.

Rails 5

Has anyone been able to get this to run in Rails 5? I just get "There was an error while trying to load the gem 'dynamoid'. (Bundler::GemRequireError)" when I run rails c or rails s.

The error message is unhelpful.

Testing Rails apps on Dynamoid-- How??

At present, I'm hacking around trying to figure out how to test my Rails app built on Dyanmoid/Dynamo... not intuitive.

Any suggestions from the pros? Thanks!

(I'm happy to consolidate suggestions and add to the README!)

eval_limit and batch don't do what I expect

I'm not sure what the chain eval_limit and batch should do e.g.

`SomeModel.eval_limit(5).batch(5)`

Should that get 5 records, in a batch of 5, or 5 batches of 5 records each?

When I tried it it scanned the entire table ignoring the limit.

Multiple typos in documentation

      # @example
      #   class User
      #     include Dynamoid::Document
      #     field :email,          :string
      #     field :age,            :integer
      #     field :gender,         :string
      #     field :rank            :number
      #     table :key => :email
      #     global_secondary_index :hash_key => :age, :range_key => :gender
      #   end
      #   User.find_all_by_secondary_index(:age => 5, :range => {"rank.lte" => 10})
  1. The :range_key => :gender should instead be :range_key => :rank.
  2. Curly braces are required around the first hash param in:
    User.find_all_by_secondary_index(:age => 5, :range => {"rank.lte" => 10}), as
    User.find_all_by_secondary_index({:age => 5}, :range => {"rank.lte" => 10})

Typecasting raises exceptions before validations get to run

class User
  include Dynamoid::Document
  field :age, :integer

  validates :numericality => {:allow_blank => true, :only_integer => true }
end

# This is raises an exception about "abc" not being an integer
u = User.new(:age => "abc")

ActiveRecord attempts to typecast and if it fails it stores nil. It also stores age_before_type_cast. Something similar needs to be done for Dynamoid.

More info here
http://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/BeforeTypeCast.html

limit method does not exist

The documentation states

 `Address.limit(10_000).batch(100)`

But limit is not a valid method. It should be eval_limit.

Range finding doesn't work as documented

For

class MyObject
  include Dynamoid::Document
  field :name
end

this:

MyObject.where("created_at.gt" => DateTime.now - 1.day).all

yields

ArgumentError: unsupported type, expected Hash, Array, Set, String, Numeric, IO, true, false, or nil, got DateTime

Am I missing something?

Dynamoid does not fail correctly if it cannot connect to an endpoint

Given the configured endpoint is not reachable:

Because of an unconditional rescue in concurrent-ruby-0.9.2/lib/concurrent/atom.rb, line 97 the ECONNREFUSED exception created in dynamoid-1.0.0/lib/dynamoid/adapter.rb, line 20 is catched and ignored.

Therefore, when I cannot connect to an endpoint, I get an undefined methodinclude?' for nil:NilClass`

To reproduce, just try to save any object with an endpoint of "localhost:3000" without DynamoDB running.

I am not sure if this should be fixed in concurrent-ruby or here. Maybe it would be a good idea, to query the tables first, then swap it in.

Not able to find 'x-amzn-requestid' when ConditionalCheckFailedException happen

We are using the Dynamoid Gem in our project
We are currectly want to implement Exception handling from the Dynamoid
Different types of exceptions are handled by
begin
// some code where exception may happen
rescue Exception => e
p e.message
end
For ConditionalCheckFailedException we are not able to get e.http_response.headers["x-amzn-requestid"]

Dynamoid should support DynamoDB attribute types byond string and number

Dynamoid translates its field types to DynamoDB attribute types, in Dynamoid::Persistence#dynamo_type.

The mapping right now is overly simple: all fields map either to a numeric or a string attribute. This maps container fields (:array and :set) to a string also.

AWS SDK v2 offers a better selection of attribute types that map much more closely to Dynamoid's field types. Dynamoid should support this.

Some specific ideas:

  • :array field => List attribute
  • :set field type should be split into :number_set and :string_set and mapped to its respective DynamoDB attribute type.

For backwards compatibility, Persistence#undump_field should check what value type AWS SDK returns, and undump the field using the old (string) behavior if the existing value is a string.

DynamoDB Expensive cost caused by the value 400 in read and write capacity

Hi,

I try this gem few months ago. I follow the examples in the documentation (readme) to create dynamoDB tables. But at the end of each month i get an expensive cost (+200$) caused by the tables created with your examples with 400 for read and write capacity.
(I forget to delete these tables because i change the region in AWS and the tables ware hidden :-( ) .

I suggest to change the capacity in your documentation to a lower value like 1 or 2. Users can adjust this value if needed.

class User
  include Dynamoid::Document
  table :name => :awesome_users, :key => :user_id, :read_capacity => 2, :write_capacity => 2
end

Thanks

Dynamic table names

I'm looking to split my data into time-sliced tables.

Before I do any work on this I wanted to ask I you would be interested in merging pull request that would allow dynamic tables names for a model (say, defined at runtime by a lambda).

Invalid item size limit calculations and MAX_ITEM_SIZE value

Dynamoid sets the MAX_ITEM_SIZE value to 64 KB. However, the current limit is 400 KB. Please see the Limits in DynamoDB documentation.

Also, the #write_attribute method within Fields incorrectly calculates this limit.

def write_attribute(name, value)
  if (size = value.to_s.size) > MAX_ITEM_SIZE
    Dynamoid.logger.warn "DynamoDB can't store items larger than #{MAX_ITEM_SIZE} and the #{name}  field has a length of #{size}."
  end
...

The calculation for the warning only takes into account the size of a single value. However the item limit is based upon the sum of all key value pairs within the item. If you wanted to keep the warning, to be accurate, the calculation would have to be something like:

attributes.map{|k,v| "#{k}#{v}"}.join.size

I can submit a pull request if you let me know the direction you would like to take.

How do I remove the timestamp fields?

Dynamoid automatically adds the fields created_at and updated_at. But I'm reading from a table that doesn't follow Rails conventions and doesn't have those fields. How do I remove them from the table definition?

A proposal of a shorter key definition

When we define hash_key and range_key with some types, we have to write as bellow:

class Comment
  include Dynamoid::Document

  table :name => :comment , :key => :user_id, :range_key => :comment_id
  range :comment_id, :integer

  field :user_id, :integer
end

This notation is a little redundant. I would rather write as:

class Comment
  include Dynamoid::Document

  table :name => :comment

  hash_key :user_id, :integer
  range_key :comment_id, :integer
end

Getting ...uninitialized constant Item::Dynamoid....error

I have already installed dynamoid gem but still getting uninitialized constant error

pi@raspberrypi:~/waterpi-web $ ruby app.rb
/home/pi/waterpi-web/models/item.rb:2:in <class:Item>': uninitialized constant Item::Dynamoid (NameError) from /home/pi/waterpi-web/models/item.rb:1:in<top (required)>'
from /home/pi/waterpi-web/boot.rb:22:in require' from /home/pi/waterpi-web/boot.rb:22:inblock (2 levels) in <top (required)>'
from /home/pi/waterpi-web/boot.rb:22:in each' from /home/pi/waterpi-web/boot.rb:22:inblock in <top (required)>'
from /home/pi/waterpi-web/boot.rb:21:in each' from /home/pi/waterpi-web/boot.rb:21:in<top (required)>'
from /home/pi/.rbenv/versions/2.3.0/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in require' from /home/pi/.rbenv/versions/2.3.0/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:inrequire'
from app.rb:3:in `

souce code- https://github.com/demirhanaydin/waterpi-web

Why no nesting of models in Dynamoid?

As I see it, the main advantage of DynamoDB (and Mongo and other document databases) is to be able to nest models in documents. E.g., to have a Person table with string fields like "name" but also fields like "address" which may itself be an object with "street" and "city" and "zip" etc.

Why does Dynamoid spread them into separate tables with foreign keys, effectively relationalizing a document database?

Best,

E

Config.namespace still adds namespacing even if set to nil

I am setting my configuration to the following:

config.adapter = 'aws_sdk_v2'
config.namespace = nil
config.warn_on_scan = true
config.read_capacity = 5
config.write_capacity = 5

However, when i get the result I am getting the following:

get_item(table_name:"_tableName",key:{"email"=>{s:"[email protected]"}})

In this example my table is called "tableName" however it is still adding the _, I tried setting config.namespace to "" and nil but it did not work.

Is this an error or did I just misunderstood the documentation?

`Dynamoid::Adapter.reconnect!` is not thread-safe

A backtrace when a program uses Dynamoid (with AWS SDK v2) in a multi-threaded environment:

 NoMethodError:
   undefined method `list_tables' for nil:NilClass
 # .../dynamoid-1.0.0pre/lib/dynamoid/adapter/aws_sdk_v2.rb:220:in `list_tables'
 # .../dynamoid-1.0.0pre/lib/dynamoid/adapter.rb:128:in `list_tables'
 # .../dynamoid-1.0.0pre/lib/dynamoid/adapter.rb:42:in `benchmark'
 # .../dynamoid-1.0.0pre/lib/dynamoid/adapter.rb:128:in `list_tables'
 # .../dynamoid-1.0.0pre/lib/dynamoid/adapter.rb:28:in `reconnect!'
 # .../dynamoid-1.0.0pre/lib/dynamoid/adapter.rb:42:in `benchmark'
 # .../dynamoid-1.0.0pre/lib/dynamoid/adapter.rb:28:in `reconnect!'
 # .../dynamoid-1.0.0pre/lib/dynamoid/adapter.rb:17:in `adapter'
 # .../dynamoid-1.0.0pre/lib/dynamoid/adapter.rb:128:in `get_item'
 # .../dynamoid-1.0.0pre/lib/dynamoid/adapter.rb:42:in `benchmark'
 # .../dynamoid-1.0.0pre/lib/dynamoid/adapter.rb:128:in `get_item'
 # .../dynamoid-1.0.0pre/lib/dynamoid/adapter.rb:80:in `read'

Here's what I think is happening:

  1. Multiple threads simultaneously call Dynamoid::Adapter.get_item.
  2. Because Dynamoid isn't connected to a DynamoDB instance yet, multiple threads invoke Dynamoid::Adapter.reconnect!. Suppose thread A and B are at the start of .reconnect!.
  3. Thread A instantiates an adapter, assigns it to @adapter, and calls @adapter.connect! (lines 26--27 of adapter.rb). Context switch to thread B.
  4. Thread B instantiates an adapter, assigns it to @adapter, but does not yet call .connect!. Context switch to thread A.
  5. Thread A calls list_tables (line 28 of adapter.rb) on an adapter instance that hasn't connected yet.

DynamoDB With Ruby ON Rails

HI,

I am having a rails application with dynamodb as database.
Ruby : 1.9.1
Ruby On Rails : 4.1
DynamoDB : 1.1

I am getting this error :

/home/lakhwani/FCLaunchRequirement/env/FCLaunchRequirementWebsite-1.0/runtime/ruby1.9/1.9.1/set.rb:85:in do_with_enum' /home/lakhwani/FCLaunchRequirement/env/FCLaunchRequirementWebsite-1.0/runtime/ruby1.9/1.9.1/set.rb:310:inmerge'
/home/lakhwani/FCLaunchRequirement/env/FCLaunchRequirementWebsite-1.0/runtime/ruby1.9/1.9.1/set.rb:75:in initialize' dynamoid (1.1.0) lib/dynamoid/persistence.rb:99:innew'
dynamoid (1.1.0) lib/dynamoid/persistence.rb:99:in undump_field' dynamoid (1.1.0) lib/dynamoid/persistence.rb:61:inblock (2 levels) in undump'
dynamoid (1.1.0) lib/dynamoid/persistence.rb:60:in each' dynamoid (1.1.0) lib/dynamoid/persistence.rb:60:inblock in undump'
dynamoid (1.1.0) lib/dynamoid/persistence.rb:59:in tap' dynamoid (1.1.0) lib/dynamoid/persistence.rb:59:inundump'
dynamoid (1.1.0) lib/dynamoid/document.rb:133:in load' dynamoid (1.1.0) lib/dynamoid/document.rb:128:inblock in initialize'
activesupport (4.1.14.2) lib/active_support/callbacks.rb:113:in call' activesupport (4.1.14.2) lib/active_support/callbacks.rb:113:incall'
activesupport (4.1.14.2) lib/active_support/callbacks.rb:552:in block (2 levels) in compile' activesupport (4.1.14.2) lib/active_support/callbacks.rb:502:incall'
activesupport (4.1.14.2) lib/active_support/callbacks.rb:502:in call' activesupport (4.1.14.2) lib/active_support/callbacks.rb:86:inrun_callbacks'
dynamoid (1.1.0) lib/dynamoid/document.rb:123:in initialize' dynamoid (1.1.0) lib/dynamoid/persistence.rb:51:innew'
dynamoid (1.1.0) lib/dynamoid/persistence.rb:51:in from_database' dynamoid (1.1.0) lib/dynamoid/identity_map.rb:15:infrom_database'
dynamoid (1.1.0) lib/dynamoid/dirty.rb:8:in from_database' dynamoid (1.1.0) lib/dynamoid/criteria/chain.rb:143:inblock (2 levels) in records_via_scan'
dynamoid (1.1.0) lib/dynamoid/adapter_plugin/aws_sdk_v2.rb:370:in <<' dynamoid (1.1.0) lib/dynamoid/adapter_plugin/aws_sdk_v2.rb:370:inblock (3 levels) in scan'
dynamoid (1.1.0) lib/dynamoid/adapter_plugin/aws_sdk_v2.rb:370:in each' dynamoid (1.1.0) lib/dynamoid/adapter_plugin/aws_sdk_v2.rb:370:inblock (2 levels) in scan'
dynamoid (1.1.0) lib/dynamoid/adapter_plugin/aws_sdk_v2.rb:367:in loop' dynamoid (1.1.0) lib/dynamoid/adapter_plugin/aws_sdk_v2.rb:367:inblock in scan'
dynamoid (1.1.0) lib/dynamoid/criteria/chain.rb:142:in each' dynamoid (1.1.0) lib/dynamoid/criteria/chain.rb:142:ineach'
dynamoid (1.1.0) lib/dynamoid/criteria/chain.rb:142:in block in records_via_scan' dynamoid (1.1.0) lib/dynamoid/criteria/chain.rb:115:ineach'
dynamoid (1.1.0) lib/dynamoid/criteria/chain.rb:115:in each' dynamoid (1.1.0) lib/dynamoid/criteria/chain.rb:115:into_a'
dynamoid (1.1.0) lib/dynamoid/criteria/chain.rb:115:in Array' dynamoid (1.1.0) lib/dynamoid/criteria/chain.rb:115:inrecords'
dynamoid (1.1.0) lib/dynamoid/criteria/chain.rb:46:in all' app/models/node.rb:931:inall_non_confidential_worldwide'
lib/brazil_config_helper.rb:162:in get_node_names' app/views/tabs/_site_information_fields.html.slim:49:in_app_views_tabs__site_information_fields_html_slim__1161341738563972731_69854304393120'
actionview (4.1.14.2) lib/action_view/template.rb:145:in block in render' activesupport (4.1.14.2) lib/active_support/notifications.rb:161:ininstrument'
actionview (4.1.14.2) lib/action_view/template.rb:339:in instrument' actionview (4.1.14.2) lib/action_view/template.rb:143:inrender'
actionview (4.1.14.2) lib/action_view/renderer/partial_renderer.rb:306:in render_partial' actionview (4.1.14.2) lib/action_view/renderer/partial_renderer.rb:279:inblock in render'
actionview (4.1.14.2) lib/action_view/renderer/abstract_renderer.rb:38:in block in instrument' activesupport (4.1.14.2) lib/active_support/notifications.rb:159:inblock in instrument'
activesupport (4.1.14.2) lib/active_support/notifications/instrumenter.rb:20:in instrument' activesupport (4.1.14.2) lib/active_support/notifications.rb:159:ininstrument'
actionview (4.1.14.2) lib/action_view/renderer/abstract_renderer.rb:38:in instrument' actionview (4.1.14.2) lib/action_view/renderer/partial_renderer.rb:278:inrender'
actionview (4.1.14.2) lib/action_view/renderer/renderer.rb:51:in render_partial' actionview (4.1.14.2) lib/action_view/helpers/rendering_helper.rb:35:inrender'
haml (4.0.0) lib/haml/helpers/action_view_mods.rb:12:in render_with_haml' app/views/tabs/_read_only_form.html.slim:11:inblock in _app_views_tabs__read_only_form_html_slim___379420859086536323_69853819859700'
app/views/tabs/_read_only_form.html.slim:8:in each' app/views/tabs/_read_only_form.html.slim:8:in_app_views_tabs__read_only_form_html_slim___379420859086536323_69853819859700'
actionview (4.1.14.2) lib/action_view/template.rb:145:in block in render' activesupport (4.1.14.2) lib/active_support/notifications.rb:161:ininstrument'
actionview (4.1.14.2) lib/action_view/template.rb:339:in instrument' actionview (4.1.14.2) lib/action_view/template.rb:143:inrender'
actionview (4.1.14.2) lib/action_view/renderer/partial_renderer.rb:306:in render_partial' actionview (4.1.14.2) lib/action_view/renderer/partial_renderer.rb:279:inblock in render'
actionview (4.1.14.2) lib/action_view/renderer/abstract_renderer.rb:38:in block in instrument' activesupport (4.1.14.2) lib/active_support/notifications.rb:159:inblock in instrument'
activesupport (4.1.14.2) lib/active_support/notifications/instrumenter.rb:20:in instrument' activesupport (4.1.14.2) lib/active_support/notifications.rb:159:ininstrument'
actionview (4.1.14.2) lib/action_view/renderer/abstract_renderer.rb:38:in instrument' actionview (4.1.14.2) lib/action_view/renderer/partial_renderer.rb:278:inrender'
actionview (4.1.14.2) lib/action_view/renderer/renderer.rb:51:in render_partial' actionview (4.1.14.2) lib/action_view/helpers/rendering_helper.rb:35:inrender'
haml (4.0.0) lib/haml/helpers/action_view_mods.rb:12:in render_with_haml' app/views/tabs/show.html.slim:7:inblock in _app_views_tabs_show_html_slim___2573104391332512245_69853756219540'
actionview (4.1.14.2) lib/action_view/helpers/capture_helper.rb:38:in block in capture' actionview (4.1.14.2) lib/action_view/helpers/capture_helper.rb:202:inwith_output_buffer'
haml (4.0.0) lib/haml/helpers/action_view_xss_mods.rb:5:in with_output_buffer_with_haml_xss' actionview (4.1.14.2) lib/action_view/helpers/capture_helper.rb:38:incapture'
haml (4.0.0) lib/haml/helpers/action_view_mods.rb:59:in capture_with_haml' actionview (4.1.14.2) lib/action_view/helpers/form_helper.rb:434:inform_for'
haml (4.0.0) lib/haml/helpers/action_view_mods.rb:135:in form_for_with_haml' haml (4.0.0) lib/haml/helpers/action_view_xss_mods.rb:28:inform_for_with_haml_xss'
app/views/tabs/show.html.slim:6:in _app_views_tabs_show_html_slim___2573104391332512245_69853756219540' actionview (4.1.14.2) lib/action_view/template.rb:145:inblock in render'
activesupport (4.1.14.2) lib/active_support/notifications.rb:161:in instrument' actionview (4.1.14.2) lib/action_view/template.rb:339:ininstrument'
actionview (4.1.14.2) lib/action_view/template.rb:143:in render' actionview (4.1.14.2) lib/action_view/renderer/template_renderer.rb:55:inblock (2 levels) in render_template'
actionview (4.1.14.2) lib/action_view/renderer/abstract_renderer.rb:38:in block in instrument' activesupport (4.1.14.2) lib/active_support/notifications.rb:159:inblock in instrument'
activesupport (4.1.14.2) lib/active_support/notifications/instrumenter.rb:20:in instrument' activesupport (4.1.14.2) lib/active_support/notifications.rb:159:ininstrument'
actionview (4.1.14.2) lib/action_view/renderer/abstract_renderer.rb:38:in instrument' actionview (4.1.14.2) lib/action_view/renderer/template_renderer.rb:54:inblock in render_template'
actionview (4.1.14.2) lib/action_view/renderer/template_renderer.rb:62:in render_with_layout' actionview (4.1.14.2) lib/action_view/renderer/template_renderer.rb:53:inrender_template'
actionview (4.1.14.2) lib/action_view/renderer/template_renderer.rb:17:in render' actionview (4.1.14.2) lib/action_view/renderer/renderer.rb:46:inrender_template'
actionview (4.1.14.2) lib/action_view/renderer/renderer.rb:27:in render' actionview (4.1.14.2) lib/action_view/rendering.rb:99:in_render_template'
actionpack (4.1.14.2) lib/action_controller/metal/streaming.rb:217:in _render_template' actionview (4.1.14.2) lib/action_view/rendering.rb:82:inrender_to_body'
actionpack (4.1.14.2) lib/action_controller/metal/rendering.rb:32:in render_to_body' actionpack (4.1.14.2) lib/action_controller/metal/renderers.rb:32:inrender_to_body'
actionpack (4.1.14.2) lib/abstract_controller/rendering.rb:25:in render' actionpack (4.1.14.2) lib/action_controller/metal/rendering.rb:16:inrender'
actionpack (4.1.14.2) lib/action_controller/metal/instrumentation.rb:44:in block (2 levels) in render' activesupport (4.1.14.2) lib/active_support/core_ext/benchmark.rb:12:inblock in ms'
/home/lakhwani/FCLaunchRequirement/env/FCLaunchRequirementWebsite-1.0/runtime/ruby1.9/1.9.1/benchmark.rb:295:in realtime' activesupport (4.1.14.2) lib/active_support/core_ext/benchmark.rb:12:inms'
actionpack (4.1.14.2) lib/action_controller/metal/instrumentation.rb:44:in block in render' actionpack (4.1.14.2) lib/action_controller/metal/instrumentation.rb:87:incleanup_view_runtime'
actionpack (4.1.14.2) lib/action_controller/metal/instrumentation.rb:43:in render' app/controllers/nodes_controller.rb:43:inblock (2 levels) in show'
actionpack (4.1.14.2) lib/action_controller/metal/mime_responds.rb:258:in call' actionpack (4.1.14.2) lib/action_controller/metal/mime_responds.rb:258:inrespond_to'
app/controllers/nodes_controller.rb:42:in show' actionpack (4.1.14.2) lib/action_controller/metal/implicit_render.rb:4:insend_action'
actionpack (4.1.14.2) lib/abstract_controller/base.rb:189:in process_action' actionpack (4.1.14.2) lib/action_controller/metal/rendering.rb:10:inprocess_action'
actionpack (4.1.14.2) lib/abstract_controller/callbacks.rb:20:in block in process_action' activesupport (4.1.14.2) lib/active_support/callbacks.rb:113:incall'
activesupport (4.1.14.2) lib/active_support/callbacks.rb:113:in call' activesupport (4.1.14.2) lib/active_support/callbacks.rb:552:inblock (2 levels) in compile'
activesupport (4.1.14.2) lib/active_support/callbacks.rb:502:in call' activesupport (4.1.14.2) lib/active_support/callbacks.rb:502:incall'
activesupport (4.1.14.2) lib/active_support/callbacks.rb:86:in run_callbacks' actionpack (4.1.14.2) lib/abstract_controller/callbacks.rb:19:inprocess_action'
actionpack (4.1.14.2) lib/action_controller/metal/rescue.rb:29:in process_action' actionpack (4.1.14.2) lib/action_controller/metal/instrumentation.rb:32:inblock in process_action'
activesupport (4.1.14.2) lib/active_support/notifications.rb:159:in block in instrument' activesupport (4.1.14.2) lib/active_support/notifications/instrumenter.rb:20:ininstrument'
activesupport (4.1.14.2) lib/active_support/notifications.rb:159:in instrument' actionpack (4.1.14.2) lib/action_controller/metal/instrumentation.rb:30:inprocess_action'
actionpack (4.1.14.2) lib/action_controller/metal/params_wrapper.rb:250:in process_action' actionpack (4.1.14.2) lib/abstract_controller/base.rb:136:inprocess'
actionview (4.1.14.2) lib/action_view/rendering.rb:30:in process' actionpack (4.1.14.2) lib/action_controller/metal.rb:196:indispatch'
actionpack (4.1.14.2) lib/action_controller/metal/rack_delegation.rb:13:in dispatch' actionpack (4.1.14.2) lib/action_controller/metal.rb:232:inblock in action'
actionpack (4.1.14.2) lib/action_dispatch/routing/route_set.rb:80:in call' actionpack (4.1.14.2) lib/action_dispatch/routing/route_set.rb:80:indispatch'
actionpack (4.1.14.2) lib/action_dispatch/routing/route_set.rb:48:in call' actionpack (4.1.14.2) lib/action_dispatch/journey/router.rb:73:inblock in call'
actionpack (4.1.14.2) lib/action_dispatch/journey/router.rb:59:in each' actionpack (4.1.14.2) lib/action_dispatch/journey/router.rb:59:incall'
actionpack (4.1.14.2) lib/action_dispatch/routing/route_set.rb:690:in call' rack (1.5.2) lib/rack/etag.rb:23:incall'
rack (1.5.2) lib/rack/conditionalget.rb:25:in call' rack (1.5.2) lib/rack/head.rb:11:incall'
actionpack (4.1.14.2) lib/action_dispatch/middleware/params_parser.rb:27:in call' actionpack (4.1.14.2) lib/action_dispatch/middleware/flash.rb:254:incall'
rack (1.5.2) lib/rack/session/abstract/id.rb:225:in context' rack (1.5.2) lib/rack/session/abstract/id.rb:220:incall'
actionpack (4.1.14.2) lib/action_dispatch/middleware/cookies.rb:562:in call' actionpack (4.1.14.2) lib/action_dispatch/middleware/callbacks.rb:29:inblock in call'
activesupport (4.1.14.2) lib/active_support/callbacks.rb:82:in run_callbacks' actionpack (4.1.14.2) lib/action_dispatch/middleware/callbacks.rb:27:incall'
actionpack (4.1.14.2) lib/action_dispatch/middleware/reloader.rb:73:in call' actionpack (4.1.14.2) lib/action_dispatch/middleware/remote_ip.rb:76:incall'
actionpack (4.1.14.2) lib/action_dispatch/middleware/debug_exceptions.rb:17:in call' actionpack (4.1.14.2) lib/action_dispatch/middleware/show_exceptions.rb:30:incall'
railties (4.1.14.2) lib/rails/rack/logger.rb:38:in call_app' railties (4.1.14.2) lib/rails/rack/logger.rb:20:inblock in call'
activesupport (4.1.14.2) lib/active_support/tagged_logging.rb:68:in block in tagged' activesupport (4.1.14.2) lib/active_support/tagged_logging.rb:26:intagged'
activesupport (4.1.14.2) lib/active_support/tagged_logging.rb:68:in tagged' railties (4.1.14.2) lib/rails/rack/logger.rb:20:incall'
/opt/brazil-pkg-cache/packages/AmazonRailsLogger/AmazonRailsLogger-4.0.80.62/RHEL5_64/DEV.STD.PTHREAD/build/ruby1.9/site_ruby/1.9.1/amazon/set_request_id.rb:9:in call' /opt/brazil-pkg-cache/packages/AmazonRailsLogger/AmazonRailsLogger-4.0.80.62/RHEL5_64/DEV.STD.PTHREAD/build/ruby1.9/site_ruby/1.9.1/amazon/set_request_id.rb:9:incall'
actionpack (4.1.14.2) lib/action_dispatch/middleware/request_id.rb:21:in call' rack (1.5.2) lib/rack/methodoverride.rb:21:incall'
rack (1.5.2) lib/rack/runtime.rb:17:in call' activesupport (4.1.14.2) lib/active_support/cache/strategy/local_cache_middleware.rb:26:incall'
rack (1.5.2) lib/rack/lock.rb:17:in call' actionpack (4.1.14.2) lib/action_dispatch/middleware/static.rb:84:incall'
rack (1.5.2) lib/rack/sendfile.rb:112:in call' railties (4.1.14.2) lib/rails/engine.rb:514:incall'
railties (4.1.14.2) lib/rails/application.rb:144:in call' railties (4.1.14.2) lib/rails/railtie.rb:194:inpublic_send'
railties (4.1.14.2) lib/rails/railtie.rb:194:in method_missing' rack (1.5.2) lib/rack/lint.rb:49:in_call'
rack (1.5.2) lib/rack/lint.rb:37:in call' rack (1.5.2) lib/rack/showexceptions.rb:24:incall'
rack (1.5.2) lib/rack/commonlogger.rb:33:in call' rack (1.5.2) lib/rack/chunked.rb:43:incall'
rack (1.5.2) lib/rack/content_length.rb:14:in call' rack (1.5.2) lib/rack/handler/webrick.rb:60:inservice'
/home/lakhwani/FCLaunchRequirement/env/FCLaunchRequirementWebsite-1.0/runtime/ruby1.9/1.9.1/webrick/httpserver.rb:138:in service' /home/lakhwani/FCLaunchRequirement/env/FCLaunchRequirementWebsite-1.0/runtime/ruby1.9/1.9.1/webrick/httpserver.rb:94:inrun'
/home/lakhwani/FCLaunchRequirement/env/FCLaunchRequirementWebsite-1.0/runtime/ruby1.9/1.9.1/webrick/server.rb:191:in `block in start_thread'

The error is in this line :

Node.where(:is_confidential => 'f').all 

Can anyone help me in this?

Migration from Dynamoid 0.7 to 1.1.0

Hi,

I am using Dynamoid 1.1.0 and I am getting the following error on line:
require 'dynamoid/adapter/aws_sdk'

Error :
cannot load such file -- dynamoid/adapter/aws_sdk

"Foreign key" for associations

I have a problem with relations, I'm migrating an app from node to rails, unfortunately I didn't make it so naming are not really standard; I have a model Release and a model Artist

class Release
  include Dynamoid::Document

  belongs_to :artists, class: Artist

  field :_id
  field :title

end
class Artist
  include Dynamoid::Document

  has_many :releases, class_name: :release

  field :_id
  field :name

end

The problem is that my relation field in Release is named artists instead of artists_id so i can't get it to work; it would be the foreign_key field but I'm not understanding how to do it here, I gave a look at the library but I'm not so advanced.

Thanks in advance
Chris

Aws::DynamoDB::Errors::ValidationException

Aws::DynamoDB::Errors::ValidationException: One or more parameter values were invalid: An AttributeValue may not contain an empty string

It looks like this is something Dynamoid could take care of in house. A nil or empty attribute should be omitted.

Update: I've taken a closer look and it looks as if something like DynamoidModel.new(whatever: nil, another: "") isn't a problem. I'll have to dig deeper to see what's causing this issue.

Another update: It's specifically a where(..).any? causing this. I have a duplicate? method which can end up running something like this:

NotificationEventEmail.where(notification_generator_id: 1, event_status: 'send', num_dispatch_attempts: 0, recipient: "[email protected]", subject: "").any?

That can be fixed with something that removed blank attributes like this:

NotificationEventEmail.where({notification_generator_id: 1, event_status: 'send', num_dispatch_attempts: 0, recipient: "[email protected]", subject: ""}.delete_if { |k, v| v.blank? }).any?

But I think seeing as this is a quirk of the AWS SDK, this should be done by Dynamoid like it does for inserts.

Support for querying secondary indexes

http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SecondaryIndexes.html

Right now it seems like the adapter only supports querying against the table with primary hash and range keys. The index support needs to be added back w/ support for secondary indexes (the index name, hash key, optional range key).

For now I was able to shoehorn the support in by extending the AwsSdkV2 adapter with a new method:

config.adapter = 'aws_sdk_v2_secondary_index'

module Dynamoid
  module AdapterPlugin
    # Extend Dynamoid Adapter to add support for querying Secondary Indexes
    class AwsSdkV2SecondaryIndex < AwsSdkV2
      def query_secondary_index(table_name, index_name, key_conditions, options = {})
        q = {
          table_name: table_name,
          index_name: index_name,
          key_conditions: key_conditions
        }
        Enumerator.new { |y|
          result = client.query(q)

          result.items.each { |r|
            y << result_item_to_hash(r)
          }
        }
      end
    end
  end
end
# There may be a better way to write this using Dynamoid's 'field.gt' but I couldn't figure it out
def self.find_all_by_customer_id_and_created_at_greater_than(customer_id, created_at_greater_than)
  key_conditions = {
    'customer_id' => {
      comparison_operator: 'EQ',
      attribute_value_list: [customer_id.to_i]
    },
    'created_at' => {
      comparison_operator: 'GT',
      attribute_value_list: [created_at_greater_than.to_i]
    }
  }
  Dynamoid.adapter.query_secondary_index(self.table_name, 'customer_id-created_at-index', key_conditions).collect do |item|
    from_database(item)
  end
end

Other thoughts: maybe the first step towards enhanced DynamoDB API support is a new convenience adapter method to use client.query + result_item_to_hash. The method could be used from Dynamoid::Document classes.

Nested attributes

Does Dynamoid currently support working with nested attributes

I failed to find anything in the documentation/README (or specs) that pertains to that.

Undefined method error on `index`

The console message instructing what to add to my class to index had bad syntax.

You can index this query by adding this to notificationeventemail.rb: index [:[:body, {:type=>:string}], :[:created_at, {:type=>:datetime}], :[:id, {:type=>:string}], :[:instant, {:type=>:boolean}], :[:notification_generator_id, {:type=>:integer}], :[:recipient, {:type=>:string}], :[:subject, {:type=>:string}], :[:updated_at, {:type=>:datetime}]]

I took a look at the docs, but now I'm getting a no method error for index.

RuntimeError: Error creating email notification: undefined method `index' for NotificationEventEmail:Class | ["/Users/whatever/.rvm/gems/ruby-2.0.0-p451@itglue/gems/dynamoid-1.1.0/lib/dynamoid/finders.rb:128:in `method_missing'

In my class I've just got:

index [:instant, :notification_generator_id, :recipient, :subject, :body]

Error "value must be enumerable"

Hi,

I am using Dynamoid version 1.0 and I am getting the following error while executing the line :

Line :

Node.where(:region => region).where(:is_completed => 'f').count

Error:

value must be enumerable

Logs:

/home/lakhwani/FCLaunchRequirement/env/FCLaunchRequirementWebsite-1.0/runtime/ruby1.9/1.9.1/set.rb:85:in do_with_enum' /home/lakhwani/FCLaunchRequirement/env/FCLaunchRequirementWebsite-1.0/runtime/ruby1.9/1.9.1/set.rb:310:inmerge'
/home/lakhwani/FCLaunchRequirement/env/FCLaunchRequirementWebsite-1.0/runtime/ruby1.9/1.9.1/set.rb:75:in initialize' dynamoid (1.0.0) lib/dynamoid/persistence.rb:99:innew'
dynamoid (1.0.0) lib/dynamoid/persistence.rb:99:in undump_field' dynamoid (1.0.0) lib/dynamoid/persistence.rb:61:inblock (2 levels) in undump'
dynamoid (1.0.0) lib/dynamoid/persistence.rb:60:in each' dynamoid (1.0.0) lib/dynamoid/persistence.rb:60:inblock in undump'
dynamoid (1.0.0) lib/dynamoid/persistence.rb:59:in tap' dynamoid (1.0.0) lib/dynamoid/persistence.rb:59:inundump'
dynamoid (1.0.0) lib/dynamoid/document.rb:133:in load' dynamoid (1.0.0) lib/dynamoid/document.rb:128:inblock in initialize'
activesupport (4.1.14.2) lib/active_support/callbacks.rb:113:in call' activesupport (4.1.14.2) lib/active_support/callbacks.rb:113:incall'
activesupport (4.1.14.2) lib/active_support/callbacks.rb:552:in block (2 levels) in compile' activesupport (4.1.14.2) lib/active_support/callbacks.rb:502:incall'
activesupport (4.1.14.2) lib/active_support/callbacks.rb:502:in call' activesupport (4.1.14.2) lib/active_support/callbacks.rb:86:inrun_callbacks'
dynamoid (1.0.0) lib/dynamoid/document.rb:123:in initialize' dynamoid (1.0.0) lib/dynamoid/persistence.rb:51:innew'
dynamoid (1.0.0) lib/dynamoid/persistence.rb:51:in from_database' dynamoid (1.0.0) lib/dynamoid/identity_map.rb:15:infrom_database'
dynamoid (1.0.0) lib/dynamoid/dirty.rb:8:in from_database' dynamoid (1.0.0) lib/dynamoid/criteria/chain.rb:143:inblock (2 levels) in records_via_scan'
dynamoid (1.0.0) lib/dynamoid/adapter_plugin/aws_sdk_v2.rb:362:in <<' dynamoid (1.0.0) lib/dynamoid/adapter_plugin/aws_sdk_v2.rb:362:inblock (3 levels) in scan'
dynamoid (1.0.0) lib/dynamoid/adapter_plugin/aws_sdk_v2.rb:362:in each' dynamoid (1.0.0) lib/dynamoid/adapter_plugin/aws_sdk_v2.rb:362:inblock (2 levels) in scan'
dynamoid (1.0.0) lib/dynamoid/adapter_plugin/aws_sdk_v2.rb:359:in loop' dynamoid (1.0.0) lib/dynamoid/adapter_plugin/aws_sdk_v2.rb:359:inblock in scan'
dynamoid (1.0.0) lib/dynamoid/criteria/chain.rb:142:in each' dynamoid (1.0.0) lib/dynamoid/criteria/chain.rb:142:ineach'
dynamoid (1.0.0) lib/dynamoid/criteria/chain.rb:142:in block in records_via_scan' dynamoid (1.0.0) lib/dynamoid/criteria/chain.rb:115:ineach'
dynamoid (1.0.0) lib/dynamoid/criteria/chain.rb:115:in each' dynamoid (1.0.0) lib/dynamoid/criteria/chain.rb:115:into_a'
dynamoid (1.0.0) lib/dynamoid/criteria/chain.rb:115:in Array' dynamoid (1.0.0) lib/dynamoid/criteria/chain.rb:115:inrecords'
dynamoid (1.0.0) lib/dynamoid/criteria/chain.rb:95:in each' app/models/node.rb:925:incount'
app/models/node.rb:925:in all_in_region_and_incomplete' app/controllers/nodes_controller.rb:201:inall_in_region_and_incomplete'
app/controllers/nodes_controller.rb:13:in index' actionpack (4.1.14.2) lib/action_controller/metal/implicit_render.rb:4:insend_action'
actionpack (4.1.14.2) lib/abstract_controller/base.rb:189:in process_action' actionpack (4.1.14.2) lib/action_controller/metal/rendering.rb:10:inprocess_action'
actionpack (4.1.14.2) lib/abstract_controller/callbacks.rb:20:in block in process_action' activesupport (4.1.14.2) lib/active_support/callbacks.rb:113:incall'
activesupport (4.1.14.2) lib/active_support/callbacks.rb:113:in call' activesupport (4.1.14.2) lib/active_support/callbacks.rb:552:inblock (2 levels) in compile'
activesupport (4.1.14.2) lib/active_support/callbacks.rb:502:in call' activesupport (4.1.14.2) lib/active_support/callbacks.rb:502:incall'
activesupport (4.1.14.2) lib/active_support/callbacks.rb:86:in run_callbacks' actionpack (4.1.14.2) lib/abstract_controller/callbacks.rb:19:inprocess_action'
actionpack (4.1.14.2) lib/action_controller/metal/rescue.rb:29:in process_action' actionpack (4.1.14.2) lib/action_controller/metal/instrumentation.rb:32:inblock in process_action'
activesupport (4.1.14.2) lib/active_support/notifications.rb:159:in block in instrument' activesupport (4.1.14.2) lib/active_support/notifications/instrumenter.rb:20:ininstrument'
activesupport (4.1.14.2) lib/active_support/notifications.rb:159:in instrument' actionpack (4.1.14.2) lib/action_controller/metal/instrumentation.rb:30:inprocess_action'
actionpack (4.1.14.2) lib/action_controller/metal/params_wrapper.rb:250:in process_action' actionpack (4.1.14.2) lib/abstract_controller/base.rb:136:inprocess'
actionview (4.1.14.2) lib/action_view/rendering.rb:30:in process' actionpack (4.1.14.2) lib/action_controller/metal.rb:196:indispatch'
actionpack (4.1.14.2) lib/action_controller/metal/rack_delegation.rb:13:in dispatch' actionpack (4.1.14.2) lib/action_controller/metal.rb:232:inblock in action'
actionpack (4.1.14.2) lib/action_dispatch/routing/route_set.rb:80:in call' actionpack (4.1.14.2) lib/action_dispatch/routing/route_set.rb:80:indispatch'
actionpack (4.1.14.2) lib/action_dispatch/routing/route_set.rb:48:in call' actionpack (4.1.14.2) lib/action_dispatch/journey/router.rb:73:inblock in call'
actionpack (4.1.14.2) lib/action_dispatch/journey/router.rb:59:in each' actionpack (4.1.14.2) lib/action_dispatch/journey/router.rb:59:incall'
actionpack (4.1.14.2) lib/action_dispatch/routing/route_set.rb:690:in call' rack (1.5.2) lib/rack/etag.rb:23:incall'
rack (1.5.2) lib/rack/conditionalget.rb:25:in call' rack (1.5.2) lib/rack/head.rb:11:incall'
actionpack (4.1.14.2) lib/action_dispatch/middleware/params_parser.rb:27:in call' actionpack (4.1.14.2) lib/action_dispatch/middleware/flash.rb:254:incall'
rack (1.5.2) lib/rack/session/abstract/id.rb:225:in context' rack (1.5.2) lib/rack/session/abstract/id.rb:220:incall'
actionpack (4.1.14.2) lib/action_dispatch/middleware/cookies.rb:562:in call' actionpack (4.1.14.2) lib/action_dispatch/middleware/callbacks.rb:29:inblock in call'
activesupport (4.1.14.2) lib/active_support/callbacks.rb:82:in run_callbacks' actionpack (4.1.14.2) lib/action_dispatch/middleware/callbacks.rb:27:incall'
actionpack (4.1.14.2) lib/action_dispatch/middleware/reloader.rb:73:in call' actionpack (4.1.14.2) lib/action_dispatch/middleware/remote_ip.rb:76:incall'
actionpack (4.1.14.2) lib/action_dispatch/middleware/debug_exceptions.rb:17:in call' actionpack (4.1.14.2) lib/action_dispatch/middleware/show_exceptions.rb:30:incall'
railties (4.1.14.2) lib/rails/rack/logger.rb:38:in call_app' railties (4.1.14.2) lib/rails/rack/logger.rb:20:inblock in call'
activesupport (4.1.14.2) lib/active_support/tagged_logging.rb:68:in block in tagged' activesupport (4.1.14.2) lib/active_support/tagged_logging.rb:26:intagged'
activesupport (4.1.14.2) lib/active_support/tagged_logging.rb:68:in tagged' railties (4.1.14.2) lib/rails/rack/logger.rb:20:incall'
/opt/brazil-pkg-cache/packages/AmazonRailsLogger/AmazonRailsLogger-4.0.80.55/RHEL5_64/DEV.STD.PTHREAD/build/ruby1.9/site_ruby/1.9.1/amazon/set_request_id.rb:9:in call' /opt/brazil-pkg-cache/packages/AmazonRailsLogger/AmazonRailsLogger-4.0.80.55/RHEL5_64/DEV.STD.PTHREAD/build/ruby1.9/site_ruby/1.9.1/amazon/set_request_id.rb:9:incall'
actionpack (4.1.14.2) lib/action_dispatch/middleware/request_id.rb:21:in call' rack (1.5.2) lib/rack/methodoverride.rb:21:incall'
rack (1.5.2) lib/rack/runtime.rb:17:in call' activesupport (4.1.14.2) lib/active_support/cache/strategy/local_cache_middleware.rb:26:incall'
rack (1.5.2) lib/rack/lock.rb:17:in call' actionpack (4.1.14.2) lib/action_dispatch/middleware/static.rb:84:incall'
rack (1.5.2) lib/rack/sendfile.rb:112:in call' railties (4.1.14.2) lib/rails/engine.rb:514:incall'
railties (4.1.14.2) lib/rails/application.rb:144:in call' railties (4.1.14.2) lib/rails/railtie.rb:194:inpublic_send'
railties (4.1.14.2) lib/rails/railtie.rb:194:in method_missing' rack (1.5.2) lib/rack/lint.rb:49:in_call'
rack (1.5.2) lib/rack/lint.rb:37:in call' rack (1.5.2) lib/rack/showexceptions.rb:24:incall'
rack (1.5.2) lib/rack/commonlogger.rb:33:in call' rack (1.5.2) lib/rack/chunked.rb:43:incall'
rack (1.5.2) lib/rack/content_length.rb:14:in call' rack (1.5.2) lib/rack/handler/webrick.rb:60:inservice'
/home/lakhwani/FCLaunchRequirement/env/FCLaunchRequirementWebsite-1.0/runtime/ruby1.9/1.9.1/webrick/httpserver.rb:138:in service' /home/lakhwani/FCLaunchRequirement/env/FCLaunchRequirementWebsite-1.0/runtime/ruby1.9/1.9.1/webrick/httpserver.rb:94:inrun'
/home/lakhwani/FCLaunchRequirement/env/FCLaunchRequirementWebsite-1.0/runtime/ruby1.9/1.9.1/webrick/server.rb:191:in `block in start_thread'

Can any one please help me in this issue?

Secondary Global Index Support

What happened to support for ...

global_secondary_index :hash_key => :name, :range_key => :posted_at
global_secondary_index :hash_key => :length`

I noticed that its included in the test spec spec/app/models/post.rb, but I don't see it in the Dynamoid::Document module.

Thanks!

Support for Un-specified Attributes

Sorry if this is the wrong place to put this,

I am working on a project where we intend to use DynamoDB as sort of an "attribute bucket" for any sort of attributes the user can dream of. We would like to use Dynamoid because it's an excellent, full-featured ORM, however the docs don't mention any support of unspecified attributes. Requiring a code-change to add a novel attribute to an object is not feasible.

With Dynamoid, is there a way to add attributes that aren't specified in the model definition? Or am I out of luck? Or is that a feature that is intending to be added (in which case I plan to contribute)?

Don't gives correct count

User.count == 9
User.all.count == 24 

Last query is working as expected But first one gives wrong result

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.