AWS Developer Tools Blog
General Availability Release of the aws-record Gem
Today, we’re pleased to announce the GA release of version 1.0.0
of the aws-record
gem.
What Is aws-record?
In version 1 of the AWS SDK for Ruby, the AWS::Record
class provided a data mapping abstraction over Amazon DynamoDB operations. Earlier this year, we released the aws-record
developer preview as a separately packaged library to provide a similar data mapping abstraction for DynamoDB, built on top of the AWS SDK for Ruby version 2. After customer feedback and some more development work, we’re pleased to move the library out of developer preview to general availability.
How to Include the aws-record Gem in Your Project
The aws-record
gem is available now from RubyGems:
gem install aws-record
You can also include it in your project’s Gemfile:
# Gemfile gem 'aws-record', '~> 1.0'
This automatically includes a dependency on the aws-sdk-resources
gem, major version 2. Be sure to include the aws-sdk
or aws-sdk-resources
gem in your Gemfile if you need to lock to a specific version, like so:
# Gemfile gem 'aws-record', '~> 1.0' gem 'aws-sdk-resources', '~> 2.5'
Working with DynamoDB Tables Using the aws-record Gem
Defining an Aws::Record Model
The aws-record
gem provides the Aws::Record
module, which you can include in a class definition. This decorates your class with a variety of helper methods that can simplify interactions with Amazon DynamoDB. For example, the following model uses a variety of preset attribute definition helper methods and attribute options:
require 'aws-record' class Forum include Aws::Record string_attr :forum_uuid, hash_key: true integer_attr :post_id, range_key: true string_attr :author_username string_attr :post_title string_attr :post_body string_set_attr :tags, default_value: Set.new datetime_attr :created_at, database_attribute_name: "PostCreatedAtTime" boolean_attr :moderation, default_value: false end
Using Validation Libraries with an Aws::Record Model
The aws-record
gem does not come with a built-in validation process. Rather, it is designed to be a persistence layer, and to allow you to bring your own validation library. For example, the following model includes the popular ActiveModel::Validations
module, and has defined a set of validations that will be run when we attempt to save an item:
require 'aws-record' require 'active_model' class Forum include Aws::Record include ActiveModel::Validations string_attr :forum_uuid, hash_key: true integer_attr :post_id, range_key: true string_attr :author_username string_attr :post_title string_attr :post_body string_set_attr :tags, default_value: Set.new datetime_attr :created_at, database_attribute_name: "PostCreatedAtTime" boolean_attr :moderation, default_value: false validates_presence_of :forum_uuid, :post_id, :author_username validates_length_of :post_title, within: 4..30 validates_length_of :post_body, within: 2..5000 end
Creating a DynamoDB Table for a Model with Aws::Record::TableMigration
The aws-record
gem provides a helper class for table operations, such as migrations. If we wanted to create a table for our Forum
model in DynamoDB, we would run the following migration:
migration = Aws::Record::TableMigration.new(Forum) migration.create!( provisioned_throughput: { read_capacity_units: 5, write_capacity_units: 2 } ) migration.wait_until_available
You can write these migrations in your Rakefile
or as standalone helper scripts for your application. Because you don’t need to update your table definition for additions of non-key attributes, you may find that you’re not running migrations as often for your Aws::Record
models.
Working with DynamoDB Items Using the aws-record Gem
Creating and Persisting a New Item
Using the example model above, once it has been created in the DynamoDB remote end using Aws::Record::TableMigration
(or if it already existed in the remote end), it is simple to create and save a new item:
post = Forum.new( forum_uuid: FORUM_UUID, post_id: 1, author_username: "Author One", post_title: "Hello!", post_body: "Hello, world!" ) post.created_at = Time.now post.save # Performs a put_item call.
You can set attributes when you initialize a new item and with setter methods that are defined for you automatically.
Finding and Modifying an Item
A class-level method #find
is provided to look up items from DynamoDB using your model’s key attributes. After setting a few new attribute values, calling #save
will make an update call to DynamoDB, reflecting only the item changes you’ve made. This is important for users who are fetching items with projections (which may not include all attributes), or using single-table inheritance patterns (who may not have modeled all attributes present in a remote item), to avoid clobbering unmodeled or non-included attribute values.
post = Forum.find(forum_uuid: FORUM_UUID, post_id: 1) post.post_title = "(Removed)" post.post_body = "(Removed)" post.moderation = true post.save # Performs an update_item call on dirty attributes only.
There is also a class-level method to directly build and make an update call to DynamoDB, using key attributes to identify the item and non-key attributes to form the update expression:
Forum.update( forum_uuid: FORUM_UUID, post_id: 1, post_title: "(Removed)", post_body: "(Removed)", moderation: true )
The preceding two code examples are functionally equivalent. You’ll have the same database state after running either snippet.
A Note on Dirty Tracking
In our last example, we talked about how item updates only reflect changes to modified attributes. Users of ActiveRecord
or similar libraries will be familiar with the concept of tracking dirty attribute values, but aws-record
is a bit different. That is because DynamoDB supports collection attribute types, and in Ruby, collection types are often modified through object mutation. To properly track changes to an item when objects can be changed through mutable state, Aws::Record
items will, by default, keep deep copies of your attribute values when loading from DynamoDB. Attribute changes through mutation, like this example, will work the way you expect:
post = Forum.find(forum_uuid: FORUM_UUID, post_id: 1) post.tags.add("First") post.dirty? # => true post.save # Will call update_item with the new tags collection.
Tracking deep copies of attribute values has implications for performance and memory. You can turn off mutation tracking at the model level. If you do so, dirty tracking will still work for new object references, but will not work for mutated objects:
class NoMTModel include Aws::Record disable_mutation_tracking string_attr :key, hash_key: true string_attr :body map_attr :map end item = NoMTModel.new(key: "key", body: "body", map: {}) item.save # Will call put_item item.map[:key] = "value" item.dirty? # => false, because we won't track mutations to objects item.body = "New Body" item.dirty? # => true, because we will still notice reassignment # Will call update_item, but only update :body unless we mark map as dirty explicitly. item.save
Try the aws-record Gem Today!
We’re excited to hear about what you’re building with aws-record
. Feel free to leave your feedback in the comments, or open an issue in our GitHub repo. Read through the documentation and get started!