dynomite 1.2.4 → 1.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +13 -1
- data/README.md +16 -8
- data/docs/migrations/long-example.rb +5 -1
- data/lib/dynomite/db_config.rb +17 -3
- data/lib/dynomite/erb.rb +2 -2
- data/lib/dynomite/item.rb +16 -29
- data/lib/dynomite/migration/dsl.rb +25 -5
- data/lib/dynomite/migration/executor.rb +1 -1
- data/lib/dynomite/migration/generator.rb +1 -1
- data/lib/dynomite/migration/templates/create_table.rb +1 -0
- data/lib/dynomite/query.rb +48 -0
- data/lib/dynomite/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8a0639976cc739134d6670945d099f3584b39f88f058e056678591a42c09be84
|
4
|
+
data.tar.gz: 6ad8c28d9147a175cf940ec756d808b66a948c27be74239ab95dc3b589dd9d74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f7f4e2dcb3163c4cc60065c3d0f74b835bd4763f6295ae00d8f92576f2c57afcc969a5b4bf0c727d9b8a45ab17e52ae573319a819cf3f7c0974cf29a91b19ac
|
7
|
+
data.tar.gz: c37b011a90f943e6e04fbdfa248dee7b8bcf1dae762ae5175c976e02325e462118715d8340f5d42022e62a40ab1b77aa80af532544e255eed2c2fec89ecd6aa8
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -3,8 +3,20 @@
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
4
4
|
This project *tries* to adhere to [Semantic Versioning](http://semver.org/), even before v1.0.
|
5
5
|
|
6
|
+
## [1.2.7] - 2022-06-12
|
7
|
+
- [#23](https://github.com/tongueroo/dynomite/pull/23) #where method refactor to allow Model.index_name('index').where(...)
|
8
|
+
- [#24](https://github.com/tongueroo/dynomite/pull/24) Add get_endpoint_ip to db_config.rb
|
9
|
+
- [#26](https://github.com/tongueroo/dynomite/pull/26) change pay_per_use to pay_per_request
|
10
|
+
- [#27](https://github.com/tongueroo/dynomite/pull/27) Fixed message that tells how to install dynamodb-local
|
11
|
+
|
12
|
+
## [1.2.6]
|
13
|
+
- Implement the `PAY_PER_USE` billing mode for table creations and updates. See [DynamoDB On Demand](https://aws.amazon.com/blogs/aws/amazon-dynamodb-on-demand-no-capacity-planning-and-pay-per-request-pricing/).
|
14
|
+
|
15
|
+
## [1.2.5]
|
16
|
+
- use correct color method
|
17
|
+
|
6
18
|
## [1.2.4]
|
7
|
-
- #16 add rainbow gem dependency for
|
19
|
+
- #16 add rainbow gem dependency for color method
|
8
20
|
- #17 fix table names for models with namespaces
|
9
21
|
|
10
22
|
## [1.2.3]
|
data/README.md
CHANGED
@@ -1,9 +1,17 @@
|
|
1
1
|
# Dynomite
|
2
2
|
|
3
|
-
|
3
|
+
[](https://www.boltops.com)
|
4
|
+
|
5
|
+
NOTE: Am looking for maintainers to help with this gem. Send me an email!
|
6
|
+
|
7
|
+
IMPORTANT: The next major version of Dynomite will be ActiveModel compatible. A POC is in the [edge](https://github.com/tongueroo/dynomite/tree/edge) branch. It's still very rough and experimental. Would not recommend using it yet, but wanted to note it.
|
4
8
|
|
5
9
|
A simple wrapper library to make DynamoDB usage a little more friendly. The modeling is ActiveRecord-ish but not exactly because DynamoDB is a different type of database. Examples below explain it best:
|
6
10
|
|
11
|
+
## Jets Docs
|
12
|
+
|
13
|
+
* [Database DynamoDB](https://rubyonjets.com/docs/database/dynamodb/)
|
14
|
+
|
7
15
|
## Examples
|
8
16
|
|
9
17
|
First define a class:
|
@@ -11,7 +19,7 @@ First define a class:
|
|
11
19
|
```ruby
|
12
20
|
class Post < Dynomite::Item
|
13
21
|
# partition_key "id" # optional, defaults to id
|
14
|
-
|
22
|
+
|
15
23
|
column :id, :title, :desc
|
16
24
|
end
|
17
25
|
```
|
@@ -21,7 +29,7 @@ end
|
|
21
29
|
```ruby
|
22
30
|
post = Post.new
|
23
31
|
post = post.replace(title: "test title")
|
24
|
-
post.attrs # {"id" => "generated-id", title" => "
|
32
|
+
post.attrs # {"id" => "generated-id", title" => "test title"}
|
25
33
|
```
|
26
34
|
|
27
35
|
`post.attrs[:id]` now contain a generated unique partition_key id. Usually the partition_key is 'id'. You can set your own unique id also by specifying id.
|
@@ -112,7 +120,7 @@ post.replace
|
|
112
120
|
|
113
121
|
puts post.id # 1962DE7D852298C5CDC809C0FEF50D8262CEDF09
|
114
122
|
puts post.name # "My First Post"
|
115
|
-
```
|
123
|
+
```
|
116
124
|
|
117
125
|
Note that any column not defined using the `column` method can still be accessed using the `attrs`
|
118
126
|
method.
|
@@ -125,15 +133,15 @@ Just add `include ActiveModel::Validations` at the top of your item class.
|
|
125
133
|
```ruby
|
126
134
|
class Post < Dynomite::Item
|
127
135
|
include ActiveModel::Validations
|
128
|
-
|
136
|
+
|
129
137
|
column :id, :name # needed
|
130
|
-
|
138
|
+
|
131
139
|
validates :id, presence: true
|
132
140
|
validates :name, presence: true
|
133
141
|
end
|
134
|
-
```
|
142
|
+
```
|
135
143
|
|
136
|
-
**Be sure to define all validated columns using `column` method**.
|
144
|
+
**Be sure to define all validated columns using `column` method**.
|
137
145
|
|
138
146
|
Validations are executed by default as soon as you call the `replace` method, returning `false` on
|
139
147
|
failure. It also can be ran manually using the `valid?` method just like with ActiveRecord models.
|
@@ -55,6 +55,9 @@ class CreateCommentsMigration < Dynomite::Migration
|
|
55
55
|
# read_capacity_units: 5,
|
56
56
|
# write_capacity_units: 5
|
57
57
|
# )
|
58
|
+
|
59
|
+
# set the billing mode to on-demand (NOTE: this overrides provisioned_throughput)
|
60
|
+
# t.billing_mode(:pay_per_request)
|
58
61
|
end
|
59
62
|
end
|
60
63
|
end
|
@@ -62,6 +65,8 @@ end
|
|
62
65
|
class UpdateCommentsMigration < Dynomite::Migration
|
63
66
|
def up
|
64
67
|
update_table :comments do |t|
|
68
|
+
# You can update from provisioned_throughput to on-demand pricing
|
69
|
+
# t.billing_mode(:pay_per_request)
|
65
70
|
|
66
71
|
# t.global_secondary_index do
|
67
72
|
# t.gsi(METHOD, INDEX_NAME) do
|
@@ -120,4 +125,3 @@ class UpdateCommentsMigration < Dynomite::Migration
|
|
120
125
|
end
|
121
126
|
end
|
122
127
|
end
|
123
|
-
|
data/lib/dynomite/db_config.rb
CHANGED
@@ -42,10 +42,24 @@ module Dynomite::DbConfig
|
|
42
42
|
# This wastes less of the users time.
|
43
43
|
def check_dynamodb_local!(endpoint)
|
44
44
|
return unless endpoint && endpoint.include?("8000")
|
45
|
-
|
46
|
-
|
45
|
+
|
46
|
+
host, port = endpoint.gsub("http://", "").split(":")
|
47
|
+
ip = get_endpoint_ip(host)
|
48
|
+
unless ip
|
49
|
+
raise "You have configured your app to use DynamoDB local, but it is not running on the host: #{host}."
|
50
|
+
end
|
51
|
+
|
52
|
+
open = port_open?(ip, 8000, 0.2)
|
47
53
|
unless open
|
48
|
-
raise "You have configured your app to use DynamoDB local, but it is not running. Please start DynamoDB local. Example: brew cask
|
54
|
+
raise "You have configured your app to use DynamoDB local, but it is not running. Please start DynamoDB local. Example: brew install --cask dynamodb-local && dynamodb-local"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def get_endpoint_ip(host)
|
59
|
+
begin
|
60
|
+
IPSocket.getaddress(host)
|
61
|
+
rescue SocketError
|
62
|
+
false # Can return anything you want here
|
49
63
|
end
|
50
64
|
end
|
51
65
|
|
data/lib/dynomite/erb.rb
CHANGED
@@ -26,7 +26,7 @@ class Dynomite::Erb
|
|
26
26
|
error_info ||= e.backtrace.grep(/\(erb\)/)[0]
|
27
27
|
raise unless error_info # unable to find the (erb):xxx: error line
|
28
28
|
line = error_info.split(':')[1].to_i
|
29
|
-
log "Error evaluating ERB template on line #{line.to_s.
|
29
|
+
log "Error evaluating ERB template on line #{line.to_s.color(:red)} of: #{path.sub(/^\.\//, '').color(:green)}"
|
30
30
|
|
31
31
|
template_lines = template.split("\n")
|
32
32
|
context = 5 # lines of context
|
@@ -35,7 +35,7 @@ class Dynomite::Erb
|
|
35
35
|
template_lines[top..bottom].each_with_index do |line_content, index|
|
36
36
|
line_number = top+index+1
|
37
37
|
if line_number == line
|
38
|
-
printf("%#{spacing}d %s\n".
|
38
|
+
printf("%#{spacing}d %s\n".color(:red), line_number, line_content)
|
39
39
|
else
|
40
40
|
printf("%#{spacing}d %s\n", line_number, line_content)
|
41
41
|
end
|
data/lib/dynomite/item.rb
CHANGED
@@ -4,6 +4,7 @@ require "digest"
|
|
4
4
|
require "yaml"
|
5
5
|
|
6
6
|
require "dynomite/reserved_words"
|
7
|
+
require "dynomite/query"
|
7
8
|
|
8
9
|
# The modeling is ActiveRecord-ish but not exactly because DynamoDB is a
|
9
10
|
# different type of database.
|
@@ -170,9 +171,17 @@ module Dynomite
|
|
170
171
|
resp.items.map {|i| self.new(i) }
|
171
172
|
end
|
172
173
|
|
174
|
+
# Creates a new chainable ActiveRecord Query-style instance with a certain index_name.
|
175
|
+
#
|
176
|
+
# Post.index_name("category-index").where(category: "Drama")
|
177
|
+
#
|
178
|
+
def self.index_name(name)
|
179
|
+
_new_query.index_name(name)
|
180
|
+
end
|
181
|
+
|
173
182
|
# Translates simple query searches:
|
174
183
|
#
|
175
|
-
# Post.where(
|
184
|
+
# Post.index_name("category-index").where(category: "Drama")
|
176
185
|
#
|
177
186
|
# translates to
|
178
187
|
#
|
@@ -183,34 +192,8 @@ module Dynomite
|
|
183
192
|
# expression_attribute_values: { ":category_value" => category },
|
184
193
|
# key_condition_expression: "#category_name = :category_value",
|
185
194
|
# )
|
186
|
-
|
187
|
-
|
188
|
-
#
|
189
|
-
# Post.where({category: "Drama"}, {index_name: "category-index"})
|
190
|
-
# VS
|
191
|
-
# Post.where(category: "Drama").index_name("category-index")
|
192
|
-
def self.where(attributes, options={})
|
193
|
-
raise "attributes.size == 1 only supported for now" if attributes.size != 1
|
194
|
-
|
195
|
-
attr_name = attributes.keys.first
|
196
|
-
attr_value = attributes[attr_name]
|
197
|
-
|
198
|
-
# params = {
|
199
|
-
# expression_attribute_names: { "#category_name" => "category" },
|
200
|
-
# expression_attribute_values: { ":category_value" => "Entertainment" },
|
201
|
-
# key_condition_expression: "#category_name = :category_value",
|
202
|
-
# }
|
203
|
-
name_key, value_key = "##{attr_name}_name", ":#{attr_name}_value"
|
204
|
-
params = {
|
205
|
-
expression_attribute_names: { name_key => attr_name },
|
206
|
-
expression_attribute_values: { value_key => attr_value },
|
207
|
-
key_condition_expression: "#{name_key} = #{value_key}",
|
208
|
-
}
|
209
|
-
# Allow direct access to override params passed to dynamodb query options.
|
210
|
-
# This is is how index_name is passed:
|
211
|
-
params = params.merge(options)
|
212
|
-
|
213
|
-
query(params)
|
195
|
+
def self.where(attributes)
|
196
|
+
_new_query.where(attributes)
|
214
197
|
end
|
215
198
|
|
216
199
|
def self.replace(attrs)
|
@@ -343,5 +326,9 @@ module Dynomite
|
|
343
326
|
@attrs[name.to_s] = value
|
344
327
|
end
|
345
328
|
end
|
329
|
+
|
330
|
+
def self._new_query
|
331
|
+
Dynomite::Query.new(self, {})
|
332
|
+
end
|
346
333
|
end
|
347
334
|
end
|
@@ -1,5 +1,12 @@
|
|
1
1
|
class Dynomite::Migration
|
2
2
|
class Dsl
|
3
|
+
ATTRIBUTES = %i[
|
4
|
+
key_schema
|
5
|
+
attribute_definitions
|
6
|
+
table_name
|
7
|
+
billing_mode
|
8
|
+
].freeze
|
9
|
+
|
3
10
|
autoload :Common, "dynomite/migration/common"
|
4
11
|
autoload :BaseSecondaryIndex, "dynomite/migration/dsl/base_secondary_index"
|
5
12
|
autoload :LocalSecondaryIndex, "dynomite/migration/dsl/local_secondary_index"
|
@@ -8,15 +15,16 @@ class Dynomite::Migration
|
|
8
15
|
include Dynomite::DbConfig
|
9
16
|
include Common
|
10
17
|
|
11
|
-
attr_accessor
|
12
|
-
|
18
|
+
attr_accessor(*ATTRIBUTES)
|
19
|
+
|
13
20
|
def initialize(method_name, table_name, &block)
|
14
21
|
@method_name = method_name
|
15
22
|
@table_name = table_name
|
16
23
|
@block = block
|
17
24
|
|
18
|
-
# Dsl fills in
|
25
|
+
# Dsl fills in attributes in as methods are called within the block.
|
19
26
|
# Attributes for both create_table and updated_table:
|
27
|
+
@billing_mode = 'PROVISIONED'
|
20
28
|
@attribute_definitions = []
|
21
29
|
@provisioned_throughput = {
|
22
30
|
read_capacity_units: 5,
|
@@ -31,6 +39,14 @@ class Dynomite::Migration
|
|
31
39
|
@lsi_indexes = []
|
32
40
|
end
|
33
41
|
|
42
|
+
# t.billing_mode(:pay_per_request)
|
43
|
+
# t.billing_mode(:provisioned) # default value
|
44
|
+
def billing_mode(mode = nil)
|
45
|
+
return @billing_mode if mode.nil?
|
46
|
+
|
47
|
+
@billing_mode = mode.to_s.upcase
|
48
|
+
end
|
49
|
+
|
34
50
|
# t.gsi(:create) do |i|
|
35
51
|
# i.partition_key "category:string"
|
36
52
|
# i.sort_key "created_at:string" # optional
|
@@ -81,9 +97,10 @@ class Dynomite::Migration
|
|
81
97
|
table_name: namespaced_table_name,
|
82
98
|
key_schema: @key_schema,
|
83
99
|
attribute_definitions: @attribute_definitions,
|
84
|
-
|
100
|
+
billing_mode: @billing_mode
|
85
101
|
}
|
86
102
|
|
103
|
+
params[:provisioned_throughput] = @provisioned_throughput if @billing_mode == 'PROVISIONED'
|
87
104
|
params[:local_secondary_indexes] = lsi_secondary_index_creates unless @lsi_indexes.empty?
|
88
105
|
params[:global_secondary_indexes] = gsi_secondary_index_creates unless @gsi_indexes.empty?
|
89
106
|
params
|
@@ -95,12 +112,15 @@ class Dynomite::Migration
|
|
95
112
|
params = {
|
96
113
|
table_name: namespaced_table_name,
|
97
114
|
attribute_definitions: @attribute_definitions,
|
115
|
+
billing_mode: @billing_mode
|
98
116
|
# update table take values only some values for the "parent" table
|
99
117
|
# no key_schema, update_table does not handle key_schema for the "parent" table
|
100
118
|
}
|
101
119
|
# only set "parent" table provisioned_throughput if user actually invoked
|
102
120
|
# it in the dsl
|
103
|
-
|
121
|
+
if @provisioned_throughput_set_called && @billing_mode == 'PROVISIONED'
|
122
|
+
params[:provisioned_throughput] = @provisioned_throughput
|
123
|
+
end
|
104
124
|
params[:global_secondary_index_updates] = global_secondary_index_updates
|
105
125
|
params
|
106
126
|
end
|
@@ -31,7 +31,7 @@ class Dynomite::Migration
|
|
31
31
|
|
32
32
|
puts "DynamoDB Table: #{@table_name} Status: #{result.table_description.table_status}"
|
33
33
|
rescue Aws::DynamoDB::Errors::ServiceError => error
|
34
|
-
puts "Unable to #{@method_name.to_s.gsub('_',' ')}: #{error.message}".
|
34
|
+
puts "Unable to #{@method_name.to_s.gsub('_',' ')}: #{error.message}".color(:red)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
@@ -31,7 +31,7 @@ class Dynomite::Migration
|
|
31
31
|
table_name: table_name,
|
32
32
|
partition_key: @options[:partition_key],
|
33
33
|
sort_key: @options[:sort_key],
|
34
|
-
provisioned_throughput: @options[:provisioned_throughput] || 5
|
34
|
+
provisioned_throughput: @options[:provisioned_throughput] || 5
|
35
35
|
)
|
36
36
|
end
|
37
37
|
|
@@ -5,6 +5,7 @@ class <%= @migration_class_name %> < Dynomite::Migration
|
|
5
5
|
<% if @sort_key # so extra spaces are not added when generated -%>
|
6
6
|
t.sort_key "<%= @sort_key %>" # optional
|
7
7
|
<% end -%>
|
8
|
+
t.billing_mode(:PROVISIONED)
|
8
9
|
t.provisioned_throughput(<%= @provisioned_throughput %>) # sets both read and write, defaults to 5 when not set
|
9
10
|
|
10
11
|
# Instead of using partition_key and sort_key you can set the
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Dynomite
|
2
|
+
class Query
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize(item, params)
|
6
|
+
@item = item
|
7
|
+
@params = params
|
8
|
+
end
|
9
|
+
|
10
|
+
def <<(item)
|
11
|
+
raise NotImplementedError
|
12
|
+
end
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
"#<Dynomite::Query [#{first(2).map(&:inspect).join(', ')}, ...]>"
|
16
|
+
end
|
17
|
+
|
18
|
+
def each(&block)
|
19
|
+
run_query.each(&block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def index_name(name)
|
23
|
+
self.class.new(@item, @params.merge(index_name: name))
|
24
|
+
end
|
25
|
+
|
26
|
+
def where(attributes)
|
27
|
+
raise "attributes.size == 1 only supported for now" if attributes.size != 1
|
28
|
+
|
29
|
+
attr_name = attributes.keys.first
|
30
|
+
attr_value = attributes[attr_name]
|
31
|
+
|
32
|
+
name_key, value_key = "##{attr_name}_name", ":#{attr_name}_value"
|
33
|
+
params = {
|
34
|
+
expression_attribute_names: { name_key => attr_name },
|
35
|
+
expression_attribute_values: { value_key => attr_value },
|
36
|
+
key_condition_expression: "#{name_key} = #{value_key}",
|
37
|
+
}
|
38
|
+
|
39
|
+
self.class.new(@item, @params.merge(params))
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def run_query
|
45
|
+
@query ||= @item.query(@params)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/dynomite/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynomite
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tung Nguyen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-06-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -129,6 +129,7 @@ files:
|
|
129
129
|
- lib/dynomite/migration/generator.rb
|
130
130
|
- lib/dynomite/migration/templates/create_table.rb
|
131
131
|
- lib/dynomite/migration/templates/update_table.rb
|
132
|
+
- lib/dynomite/query.rb
|
132
133
|
- lib/dynomite/reserved_words.rb
|
133
134
|
- lib/dynomite/version.rb
|
134
135
|
homepage: https://github.com/tongueroo/dynomite
|
@@ -149,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
149
150
|
- !ruby/object:Gem::Version
|
150
151
|
version: '0'
|
151
152
|
requirements: []
|
152
|
-
rubygems_version: 3.
|
153
|
+
rubygems_version: 3.3.12
|
153
154
|
signing_key:
|
154
155
|
specification_version: 4
|
155
156
|
summary: ActiveRecord-ish Dynamodb Model
|