Class: Dynomite::Item
- Inherits:
-
Object
- Object
- Dynomite::Item
- Defined in:
- lib/dynomite/item.rb
Class Method Summary collapse
- .add_column(name) ⇒ Object
-
.column(*names) ⇒ Object
Defines column.
- .count ⇒ Object
-
.delete(key_object, options = {}) ⇒ Object
Two ways to use the delete method:.
- .find(id) ⇒ Object
- .get_table_name ⇒ Object
-
.partition_key(*args) ⇒ Object
When called with an argument we’ll set the internal @partition_key value When called without an argument just retun it.
-
.query(params = {}) ⇒ Object
Adds very little wrapper logic to query.
- .replace(attrs) ⇒ Object
-
.scan(params = {}) ⇒ Object
Adds very little wrapper logic to scan.
- .set_table_name(value) ⇒ Object
- .table ⇒ Object
- .table_name(*args) ⇒ Object
-
.where(attributes, options = {}) ⇒ Object
Translates simple query searches:.
Instance Method Summary collapse
-
#as_json(options = {}) ⇒ Object
For render json: item.
- #attributes ⇒ Object
-
#attributes=(attributes) ⇒ Object
Longer hand methods for completeness.
-
#attrs(*args) ⇒ Object
Defining our own reader so we can do a deep merge if user passes in attrs.
- #delete ⇒ Object
- #find(id) ⇒ Object
-
#initialize(attrs = {}) ⇒ Item
constructor
A new instance of Item.
- #partition_key ⇒ Object
-
#replace(hash = {}) ⇒ Object
The method is named replace to clearly indicate that the item is fully replaced.
-
#replace!(hash = {}) ⇒ Object
Similar to replace, but raises an error on failed validation.
- #table_name ⇒ Object
Methods included from DbConfig
#db, included, #namespaced_table_name
Methods included from Log
Constructor Details
#initialize(attrs = {}) ⇒ Item
Returns a new instance of Item.
45 46 47 |
# File 'lib/dynomite/item.rb', line 45 def initialize(attrs={}) @attrs = attrs end |
Class Method Details
.add_column(name) ⇒ Object
323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/dynomite/item.rb', line 323 def self.add_column(name) raise "'#{name}' is a reserved word" if Dynomite::RESERVED_WORDS.include?(name) define_method(name) do @attrs[name.to_s] end define_method("#{name}=") do |value| @attrs[name.to_s] = value end end |
.column(*names) ⇒ Object
Defines column. Defined column can be accessed by getter and setter methods of the same name (e.g. [model.my_column]). Attributes with undefined columns can be accessed by
- model.attrs
-
method.
318 319 320 |
# File 'lib/dynomite/item.rb', line 318 def self.column(*names) names.each(&method(:add_column)) end |
.count ⇒ Object
311 312 313 |
# File 'lib/dynomite/item.rb', line 311 def self.count table.item_count end |
.delete(key_object, options = {}) ⇒ Object
Two ways to use the delete method:
-
Specify the key as a String. In this case the key will is the partition_key
set on the model.
MyModel.delete("728e7b5df40b93c3ea6407da8ac3e520e00d7351")
-
Specify the key as a Hash, you can arbitrarily specific the key structure this way
MyModel.delete(“728e7b5df40b93c3ea6407da8ac3e520e00d7351”)
options is provided in case you want to specific condition_expression or expression_attribute_values.
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/dynomite/item.rb', line 256 def self.delete(key_object, ={}) if key_object.is_a?(String) key = { partition_key => key_object } else # it should be a Hash key = key_object end params = { table_name: table_name, key: key } # In case you want to specify condition_expression or expression_attribute_values params = params.merge() resp = db.delete_item(params) end |
.find(id) ⇒ Object
236 237 238 239 240 241 242 243 |
# File 'lib/dynomite/item.rb', line 236 def self.find(id) resp = db.get_item( table_name: table_name, key: {partition_key => id} ) attributes = resp.item # unwraps the item's attributes self.new(attributes) if attributes end |
.get_table_name ⇒ Object
298 299 300 301 |
# File 'lib/dynomite/item.rb', line 298 def self.get_table_name @table_name ||= self.name.pluralize.underscore [table_namespace, @table_name].reject {|s| s.nil? || s.empty?}.join('-') end |
.partition_key(*args) ⇒ Object
When called with an argument we’ll set the internal @partition_key value When called without an argument just retun it. class Comment < Dynomite::Item
partition_key "post_id"
end
280 281 282 283 284 285 286 287 |
# File 'lib/dynomite/item.rb', line 280 def self.partition_key(*args) case args.size when 0 @partition_key || "id" # defaults to id when 1 @partition_key = args[0].to_s end end |
.query(params = {}) ⇒ Object
Adds very little wrapper logic to query.
-
Automatically add table_name to options for convenience.
-
Decorates return value. Returns Array of [MyModel.new] instead of the dynamodb client response.
Other than that, usage is same was using the dynamodb client query method directly. Example:
MyModel.query(
index_name: 'category-index',
expression_attribute_names: { "#category_name" => "category" },
expression_attribute_values: { ":category_value" => "Entertainment" },
key_condition_expression: "#category_name = :category_value",
)
AWS Docs examples: docs.aws.amazon.com/amazondynamodb/latest/developerguide/GettingStarted.Ruby.04.html
165 166 167 168 169 |
# File 'lib/dynomite/item.rb', line 165 def self.query(params={}) params = { table_name: table_name }.merge(params) resp = db.query(params) resp.items.map {|i| self.new(i) } end |
.replace(attrs) ⇒ Object
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/dynomite/item.rb', line 214 def self.replace(attrs) # Automatically adds some attributes: # partition key unique id # created_at and updated_at timestamps. Timestamp format from AWS docs: http://amzn.to/2z98Bdc defaults = { partition_key => Digest::SHA1.hexdigest([Time.now, rand].join) } item = defaults.merge(attrs) item["created_at"] ||= Time.now.utc.strftime('%Y-%m-%dT%TZ') item["updated_at"] = Time.now.utc.strftime('%Y-%m-%dT%TZ') # put_item full replaces the item resp = db.put_item( table_name: table_name, item: item ) # The resp does not contain the attrs. So might as well return # the original item with the generated partition_key value item end |
.scan(params = {}) ⇒ Object
Adds very little wrapper logic to scan.
-
Automatically add table_name to options for convenience.
-
Decorates return value. Returns Array of [MyModel.new] instead of the dynamodb client response.
Other than that, usage is same was using the dynamodb client scan method directly. Example:
MyModel.scan(
expression_attribute_names: {"#updated_at"=>"updated_at"},
filter_expression: "#updated_at between :start_time and :end_time",
expression_attribute_values: {
":start_time" => "2010-01-01T00:00:00",
":end_time" => "2020-01-01T00:00:00"
}
)
AWS Docs examples: docs.aws.amazon.com/amazondynamodb/latest/developerguide/GettingStarted.Ruby.04.html
140 141 142 143 144 145 146 |
# File 'lib/dynomite/item.rb', line 140 def self.scan(params={}) log("It's recommended to not use scan for production. It can be slow and expensive. You can a LSI or GSI and query the index instead.") log("Scanning table: #{table_name}") params = { table_name: table_name }.merge(params) resp = db.scan(params) resp.items.map {|i| self.new(i) } end |
.set_table_name(value) ⇒ Object
303 304 305 |
# File 'lib/dynomite/item.rb', line 303 def self.set_table_name(value) @table_name = value end |
.table ⇒ Object
307 308 309 |
# File 'lib/dynomite/item.rb', line 307 def self.table Aws::DynamoDB::Table.new(name: table_name, client: db) end |
.table_name(*args) ⇒ Object
289 290 291 292 293 294 295 296 |
# File 'lib/dynomite/item.rb', line 289 def self.table_name(*args) case args.size when 0 get_table_name when 1 set_table_name(args[0]) end end |
.where(attributes, options = {}) ⇒ Object
Translates simple query searches:
Post.where({category: "Drama"}, index_name: "category-index")
translates to
resp = db.query(
table_name: "demo-dev-post",
index_name: 'category-index',
expression_attribute_names: { "#category_name" => "category" },
expression_attribute_values: { ":category_value" => category },
key_condition_expression: "#category_name = :category_value",
)
TODO: Implement nicer where syntax with index_name as a chained method.
Post.where({category: "Drama"}, {index_name: "category-index"})
VS
Post.where(category: "Drama").index_name("category-index")
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/dynomite/item.rb', line 190 def self.where(attributes, ={}) raise "attributes.size == 1 only supported for now" if attributes.size != 1 attr_name = attributes.keys.first attr_value = attributes[attr_name] # params = { # expression_attribute_names: { "#category_name" => "category" }, # expression_attribute_values: { ":category_value" => "Entertainment" }, # key_condition_expression: "#category_name = :category_value", # } name_key, value_key = "##{attr_name}_name", ":#{attr_name}_value" params = { expression_attribute_names: { name_key => attr_name }, expression_attribute_values: { value_key => attr_value }, key_condition_expression: "#{name_key} = #{value_key}", } # Allow direct access to override params passed to dynamodb query options. # This is is how index_name is passed: params = params.merge() query(params) end |
Instance Method Details
#as_json(options = {}) ⇒ Object
For render json: item
107 108 109 |
# File 'lib/dynomite/item.rb', line 107 def as_json(={}) @attrs end |
#attributes ⇒ Object
117 118 119 |
# File 'lib/dynomite/item.rb', line 117 def attributes @attributes end |
#attributes=(attributes) ⇒ Object
Longer hand methods for completeness. Internallly encourage the shorter attrs method.
113 114 115 |
# File 'lib/dynomite/item.rb', line 113 def attributes=(attributes) @attributes = attributes end |
#attrs(*args) ⇒ Object
Defining our own reader so we can do a deep merge if user passes in attrs
50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/dynomite/item.rb', line 50 def attrs(*args) case args.size when 0 ActiveSupport::HashWithIndifferentAccess.new(@attrs) when 1 attributes = args[0] # Hash if attributes.empty? ActiveSupport::HashWithIndifferentAccess.new else @attrs = attrs.deep_merge!(attributes) end end end |
#delete ⇒ Object
94 95 96 |
# File 'lib/dynomite/item.rb', line 94 def delete self.class.delete(@attrs[:id]) if @attrs[:id] end |
#find(id) ⇒ Object
90 91 92 |
# File 'lib/dynomite/item.rb', line 90 def find(id) self.class.find(id) end |
#partition_key ⇒ Object
102 103 104 |
# File 'lib/dynomite/item.rb', line 102 def partition_key self.class.partition_key end |
#replace(hash = {}) ⇒ Object
The method is named replace to clearly indicate that the item is fully replaced.
71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/dynomite/item.rb', line 71 def replace(hash={}) @attrs = @attrs.deep_merge(hash) # valid? method comes from ActiveModel::Validations if respond_to? :valid? return false unless valid? end attrs = self.class.replace(@attrs) @attrs = attrs # refresh attrs because it now has the id end |
#replace!(hash = {}) ⇒ Object
Similar to replace, but raises an error on failed validation. Works that way only if ActiveModel::Validations are included
86 87 88 |
# File 'lib/dynomite/item.rb', line 86 def replace!(hash={}) raise "Validation failed: #{errors..join(', ')}" unless replace(hash) end |
#table_name ⇒ Object
98 99 100 |
# File 'lib/dynomite/item.rb', line 98 def table_name self.class.table_name end |