dynomite 1.2.7 → 2.0.1
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 +17 -2
- data/CHANGELOG.md +21 -0
- data/Gemfile +1 -5
- data/LICENSE.txt +22 -0
- data/README.md +6 -190
- data/Rakefile +13 -1
- data/dynomite.gemspec +9 -2
- data/exe/dynomite +14 -0
- data/lib/dynomite/associations/association.rb +126 -0
- data/lib/dynomite/associations/belongs_to.rb +35 -0
- data/lib/dynomite/associations/has_and_belongs_to_many.rb +19 -0
- data/lib/dynomite/associations/has_many.rb +19 -0
- data/lib/dynomite/associations/has_one.rb +19 -0
- data/lib/dynomite/associations/many_association.rb +257 -0
- data/lib/dynomite/associations/single_association.rb +157 -0
- data/lib/dynomite/associations.rb +248 -0
- data/lib/dynomite/autoloader.rb +25 -0
- data/lib/dynomite/cli.rb +48 -0
- data/lib/dynomite/client.rb +118 -0
- data/lib/dynomite/command.rb +89 -0
- data/lib/dynomite/completer/script.rb +6 -0
- data/lib/dynomite/completer/script.sh +10 -0
- data/lib/dynomite/completer.rb +159 -0
- data/lib/dynomite/config.rb +39 -0
- data/lib/dynomite/core.rb +18 -19
- data/lib/dynomite/engine.rb +15 -0
- data/lib/dynomite/erb.rb +5 -3
- data/lib/dynomite/error.rb +12 -0
- data/lib/dynomite/help/completion.md +20 -0
- data/lib/dynomite/help/completion_script.md +3 -0
- data/lib/dynomite/help/migrate.md +3 -0
- data/lib/dynomite/help.rb +9 -0
- data/lib/dynomite/install.rb +4 -0
- data/lib/dynomite/item/abstract.rb +15 -0
- data/lib/dynomite/item/components.rb +33 -0
- data/lib/dynomite/item/dsl.rb +101 -0
- data/lib/dynomite/item/id.rb +41 -0
- data/lib/dynomite/item/indexes/finder.rb +58 -0
- data/lib/dynomite/item/indexes/index.rb +21 -0
- data/lib/dynomite/item/indexes/primary_index.rb +18 -0
- data/lib/dynomite/item/indexes.rb +25 -0
- data/lib/dynomite/item/locking.rb +53 -0
- data/lib/dynomite/item/magic_fields.rb +66 -0
- data/lib/dynomite/item/primary_key.rb +85 -0
- data/lib/dynomite/item/query/delegates.rb +28 -0
- data/lib/dynomite/item/query/params/base.rb +42 -0
- data/lib/dynomite/item/query/params/expression_attribute.rb +79 -0
- data/lib/dynomite/item/query/params/filter.rb +41 -0
- data/lib/dynomite/item/query/params/function/attribute_exists.rb +21 -0
- data/lib/dynomite/item/query/params/function/attribute_type.rb +30 -0
- data/lib/dynomite/item/query/params/function/base.rb +33 -0
- data/lib/dynomite/item/query/params/function/begins_with.rb +32 -0
- data/lib/dynomite/item/query/params/function/contains.rb +7 -0
- data/lib/dynomite/item/query/params/function/size_fn.rb +37 -0
- data/lib/dynomite/item/query/params/helpers.rb +94 -0
- data/lib/dynomite/item/query/params/key_condition.rb +34 -0
- data/lib/dynomite/item/query/params.rb +115 -0
- data/lib/dynomite/item/query/partiql/executer.rb +72 -0
- data/lib/dynomite/item/query/partiql.rb +67 -0
- data/lib/dynomite/item/query/relation/chain.rb +125 -0
- data/lib/dynomite/item/query/relation/comparision_expression.rb +21 -0
- data/lib/dynomite/item/query/relation/comparision_map.rb +19 -0
- data/lib/dynomite/item/query/relation/delete.rb +38 -0
- data/lib/dynomite/item/query/relation/ids.rb +21 -0
- data/lib/dynomite/item/query/relation/math.rb +19 -0
- data/lib/dynomite/item/query/relation/where_field.rb +32 -0
- data/lib/dynomite/item/query/relation/where_group.rb +78 -0
- data/lib/dynomite/item/query/relation.rb +127 -0
- data/lib/dynomite/item/query.rb +7 -0
- data/lib/dynomite/item/read/find.rb +196 -0
- data/lib/dynomite/item/read/find_with_event.rb +42 -0
- data/lib/dynomite/item/read.rb +90 -0
- data/lib/dynomite/item/sti.rb +43 -0
- data/lib/dynomite/item/table_namespace.rb +43 -0
- data/lib/dynomite/item/typecaster.rb +106 -0
- data/lib/dynomite/item/waiter_methods.rb +18 -0
- data/lib/dynomite/item/write/base.rb +15 -0
- data/lib/dynomite/item/write/delete_item.rb +14 -0
- data/lib/dynomite/item/write/put_item.rb +99 -0
- data/lib/dynomite/item/write/update_item.rb +73 -0
- data/lib/dynomite/item/write.rb +204 -0
- data/lib/dynomite/item.rb +113 -286
- data/lib/dynomite/migration/dsl/accessor.rb +19 -0
- data/lib/dynomite/migration/dsl/index/base.rb +42 -0
- data/lib/dynomite/migration/dsl/index/gsi.rb +59 -0
- data/lib/dynomite/migration/dsl/index/lsi.rb +27 -0
- data/lib/dynomite/migration/dsl/index.rb +72 -0
- data/lib/dynomite/migration/dsl/primary_key.rb +62 -0
- data/lib/dynomite/migration/dsl/provisioned_throughput.rb +38 -0
- data/lib/dynomite/migration/dsl.rb +89 -142
- data/lib/dynomite/migration/file_info.rb +28 -0
- data/lib/dynomite/migration/generator.rb +30 -16
- data/lib/dynomite/migration/helpers.rb +7 -0
- data/lib/dynomite/migration/internal/migrate/create_schema_migrations.rb +17 -0
- data/lib/dynomite/migration/internal/models/schema_migration.rb +6 -0
- data/lib/dynomite/migration/runner.rb +179 -0
- data/lib/dynomite/migration/templates/create_table.rb +7 -23
- data/lib/dynomite/migration/templates/delete_table.rb +7 -0
- data/lib/dynomite/migration/templates/update_table.rb +3 -18
- data/lib/dynomite/migration.rb +53 -10
- data/lib/dynomite/reserved_words.rb +13 -3
- data/lib/dynomite/seed.rb +13 -0
- data/lib/dynomite/types.rb +22 -0
- data/lib/dynomite/version.rb +1 -1
- data/lib/dynomite/waiter.rb +40 -0
- data/lib/dynomite.rb +11 -17
- data/lib/generators/application_item/application_item_generator.rb +30 -0
- data/lib/generators/application_item/templates/application_item.rb.tt +4 -0
- data/lib/jets/commands/dynamodb_command.rb +29 -0
- data/lib/jets/commands/help/generate.md +33 -0
- data/lib/jets/commands/help/migrate.md +3 -0
- metadata +201 -17
- data/docs/migrations/long-example.rb +0 -127
- data/docs/migrations/short-example.rb +0 -40
- data/lib/dynomite/db_config.rb +0 -121
- data/lib/dynomite/errors.rb +0 -15
- data/lib/dynomite/log.rb +0 -15
- data/lib/dynomite/migration/common.rb +0 -86
- data/lib/dynomite/migration/dsl/base_secondary_index.rb +0 -73
- data/lib/dynomite/migration/dsl/global_secondary_index.rb +0 -4
- data/lib/dynomite/migration/dsl/local_secondary_index.rb +0 -8
- data/lib/dynomite/migration/executor.rb +0 -38
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
require "timeout"
|
|
2
|
+
require_relative "internal/migrate/create_schema_migrations"
|
|
3
|
+
require_relative "internal/models/schema_migration"
|
|
4
|
+
|
|
5
|
+
class Dynomite::Migration
|
|
6
|
+
class Runner
|
|
7
|
+
include Dynomite::Item::WaiterMethods
|
|
8
|
+
|
|
9
|
+
def initialize(options={})
|
|
10
|
+
@options = options
|
|
11
|
+
Dynomite.config.log_level = :info unless ENV['DYNOMITE_DEBUG']
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def run
|
|
15
|
+
puts "Running Dynomite migrations"
|
|
16
|
+
ensure_schema_migrations_exist!
|
|
17
|
+
clear_error_schema_migrations! if ENV['CLEAR_ERRORS']
|
|
18
|
+
check_for_migration_errors!
|
|
19
|
+
Dynomite::Migration::FileInfo.all_files.each do |path|
|
|
20
|
+
migrate(path)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def migrate(path)
|
|
25
|
+
# load migration class definition
|
|
26
|
+
# CreatePosts#up (up is called below)
|
|
27
|
+
file_info = FileInfo.new(path)
|
|
28
|
+
|
|
29
|
+
schema_migration = SchemaMigration.find_by(version: file_info.version)
|
|
30
|
+
if schema_migration
|
|
31
|
+
teriminal_statuses = %w[complete error]
|
|
32
|
+
if teriminal_statuses.include?(schema_migration.status)
|
|
33
|
+
return
|
|
34
|
+
else
|
|
35
|
+
action = with_timeout(message: "Timed out. You must respond within 60s.") do
|
|
36
|
+
uncompleted_migration_prompt(file_info, schema_migration)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
case action
|
|
42
|
+
when :skip
|
|
43
|
+
return
|
|
44
|
+
when :delete
|
|
45
|
+
schema_migration.delete
|
|
46
|
+
when :completed
|
|
47
|
+
schema_migration.status = "completed"
|
|
48
|
+
schema_migration.save
|
|
49
|
+
return
|
|
50
|
+
when :exit
|
|
51
|
+
puts "Exiting"
|
|
52
|
+
exit
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
puts "Running migration: #{file_info.pretty_path}"
|
|
56
|
+
load path
|
|
57
|
+
|
|
58
|
+
# INSERT schema_migration table - in_progress
|
|
59
|
+
unless schema_migration
|
|
60
|
+
schema_migration = SchemaMigration.new(version: file_info.version, status: "in_progress", path: file_info.pretty_path)
|
|
61
|
+
schema_migration.save
|
|
62
|
+
end
|
|
63
|
+
start_time = Time.now
|
|
64
|
+
|
|
65
|
+
# Run actual migration
|
|
66
|
+
migration_class = file_info.migration_class
|
|
67
|
+
error_message = nil
|
|
68
|
+
begin
|
|
69
|
+
# Runs migration up command. Example:
|
|
70
|
+
# CreatePosts#up
|
|
71
|
+
# Migration#create_table
|
|
72
|
+
# Migration#execute (wait happens here)
|
|
73
|
+
migration_class.new.up # wait happens within create_table or update_table
|
|
74
|
+
rescue Aws::DynamoDB::Errors::ServiceError => error
|
|
75
|
+
puts "Unable to #{@method_name.to_s.gsub('_',' ')}: #{error.message}".color(:red)
|
|
76
|
+
error_message = error.message
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# UPDATE schema_migrations table - complete status
|
|
80
|
+
if error_message
|
|
81
|
+
schema_migration.status = "error"
|
|
82
|
+
schema_migration.error_message = error_message
|
|
83
|
+
else
|
|
84
|
+
schema_migration.status = "complete"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
schema_migration.time_took = (Time.now - start_time).to_i
|
|
88
|
+
schema_migration.save
|
|
89
|
+
# schema_migration.delete # HACK
|
|
90
|
+
puts "Time took: #{pretty_time_took(schema_migration.time_took)}"
|
|
91
|
+
exit 1 if error_message # otherwise continue to next migration file
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def with_timeout(options={}, &block)
|
|
95
|
+
seconds = options[:seconds] || 60
|
|
96
|
+
message = options[:message] || "Timed out after #{seconds}s."
|
|
97
|
+
Timeout::timeout(seconds, &block)
|
|
98
|
+
rescue Timeout::Error => e
|
|
99
|
+
puts "#{e.class}: #{e.message}"
|
|
100
|
+
puts message
|
|
101
|
+
exit 1
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def uncompleted_migration_prompt(file_info, schema_migration)
|
|
105
|
+
choice = nil
|
|
106
|
+
until %w[s d c e].include?(choice)
|
|
107
|
+
puts(<<~EOL)
|
|
108
|
+
The #{file_info.pretty_path} migration status is incomplete. Status: #{schema_migration.status}
|
|
109
|
+
This can happen and if it was interrupted by a CTRL-C.
|
|
110
|
+
Please check the migration to help determine what to do next.
|
|
111
|
+
|
|
112
|
+
Options:
|
|
113
|
+
|
|
114
|
+
s - skip and continue. leaves schema_migrations item as-is
|
|
115
|
+
d - delete and continue. deletes the schema_migrations item
|
|
116
|
+
c - mark as successful completed and continue. updates the schema_migrations item as completed.
|
|
117
|
+
e - exit
|
|
118
|
+
|
|
119
|
+
EOL
|
|
120
|
+
print "Choose an option (s/d/c/e): "
|
|
121
|
+
choice = $stdin.gets.strip
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
map = {
|
|
125
|
+
"s" => :skip,
|
|
126
|
+
"d" => :delete,
|
|
127
|
+
"c" => :completed,
|
|
128
|
+
"e" => :exit,
|
|
129
|
+
}
|
|
130
|
+
map[choice]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def ensure_schema_migrations_exist!
|
|
134
|
+
migration = CreateSchemaMigrations.new
|
|
135
|
+
return if migration.table_exist?(SchemaMigration.table_name)
|
|
136
|
+
puts "Creating #{SchemaMigration.table_name} table for the first time"
|
|
137
|
+
migration.up
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def clear_error_schema_migrations!
|
|
141
|
+
SchemaMigration.warn_on_scan(false).where(status: "error").each do |schema_migration|
|
|
142
|
+
schema_migration.delete
|
|
143
|
+
puts "Deleted error schema_migration: #{schema_migration.path}"
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def check_for_migration_errors!
|
|
148
|
+
errors = SchemaMigration.warn_on_scan(false).where(status: "error")
|
|
149
|
+
errors_info = SchemaMigration.warn_on_scan(false).where(status: "error").map do |schema_migration|
|
|
150
|
+
" #{schema_migration.path} - #{schema_migration.error_message}"
|
|
151
|
+
end.join("\n")
|
|
152
|
+
if errors.count > 0
|
|
153
|
+
puts <<~EOL
|
|
154
|
+
Found error migrations. Please review the migration erors fix before continuing.
|
|
155
|
+
You can clear them out manually by deleting them from the #{SchemaMigration.table_name} table.
|
|
156
|
+
|
|
157
|
+
#{errors_info}
|
|
158
|
+
|
|
159
|
+
You can also clear them with the following command:
|
|
160
|
+
|
|
161
|
+
CLEAR_ERRORS=1 jets dynamodb:migrate
|
|
162
|
+
|
|
163
|
+
EOL
|
|
164
|
+
exit 1
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# http://stackoverflow.com/questions/4175733/convert-duration-to-hoursminutesseconds-or-similar-in-rails-3-or-ruby
|
|
169
|
+
def pretty_time_took(total_seconds)
|
|
170
|
+
minutes = (total_seconds / 60) % 60
|
|
171
|
+
seconds = total_seconds % 60
|
|
172
|
+
if total_seconds < 60
|
|
173
|
+
"#{seconds.to_i}s"
|
|
174
|
+
else
|
|
175
|
+
"#{minutes.to_i}m #{seconds.to_i}s"
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
@@ -1,33 +1,17 @@
|
|
|
1
1
|
class <%= @migration_class_name %> < Dynomite::Migration
|
|
2
2
|
def up
|
|
3
3
|
create_table :<%= @table_name %> do |t|
|
|
4
|
-
t.partition_key
|
|
4
|
+
t.partition_key :<%= @partition_key %>
|
|
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
|
-
|
|
9
|
-
t.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
# t.key_schema([
|
|
14
|
-
# {attribute_name: "id", :key_type=>"HASH"},
|
|
15
|
-
# {attribute_name: "created_at", :key_type=>"RANGE"}
|
|
16
|
-
# ])
|
|
17
|
-
# t.attribute_definitions([
|
|
18
|
-
# {attribute_name: "id", attribute_type: "N"},
|
|
19
|
-
# {attribute_name: "created_at", attribute_type: "S"}
|
|
20
|
-
# ])
|
|
21
|
-
|
|
22
|
-
# other ways to set provisioned_throughput
|
|
23
|
-
# t.provisioned_throughput(:read, 10)
|
|
24
|
-
# t.provisioned_throughput(:write, 10)
|
|
25
|
-
# t.provisioned_throughput(
|
|
26
|
-
# read_capacity_units: 5,
|
|
27
|
-
# write_capacity_units: 5
|
|
28
|
-
# )
|
|
8
|
+
<% if %w[id id:string].include?(@partition_key) -%>
|
|
9
|
+
t.add_gsi :updated_at
|
|
10
|
+
<% else %>
|
|
11
|
+
t.add_gsi :id
|
|
12
|
+
<% end -%>
|
|
29
13
|
end
|
|
30
14
|
end
|
|
31
15
|
end
|
|
32
16
|
|
|
33
|
-
# More examples: https://
|
|
17
|
+
# More examples: https://rubyonjets.com/docs/database/dynamodb/migration/
|
|
@@ -1,26 +1,11 @@
|
|
|
1
1
|
class <%= @migration_class_name %> < Dynomite::Migration
|
|
2
2
|
def up
|
|
3
3
|
update_table :<%= @table_name %> do |t|
|
|
4
|
-
t.
|
|
5
|
-
i.partition_key "<%= @partition_key %>" # required
|
|
6
|
-
<% if @sort_key # so extra spaces are not added when generated -%>
|
|
7
|
-
t.sort_key "<%= @sort_key %>" # optional
|
|
8
|
-
<% end -%>
|
|
4
|
+
t.add_gsi(partition_key: "<%= @partition_key %>", sort_key: "<%= @sort_key || 'updated_at' %>")
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# Examples:
|
|
14
|
-
# t.gsi(:update, "update-me-index") do |i|
|
|
15
|
-
# i.provisioned_throughput(10)
|
|
16
|
-
# end
|
|
17
|
-
|
|
18
|
-
# t.gsi(:delete, "delete-me-index")
|
|
19
|
-
|
|
20
|
-
# Must use :create, :update, :delete one at a time in separate migration files.
|
|
21
|
-
# DynamoDB imposes this.
|
|
6
|
+
# t.remove_gsi(partition_key: "<%= @partition_key %>", sort_key: "<%= @sort_key || 'updated_at' %>")
|
|
22
7
|
end
|
|
23
8
|
end
|
|
24
9
|
end
|
|
25
10
|
|
|
26
|
-
# More examples: https://
|
|
11
|
+
# More examples: https://rubyonjets.com/docs/database/dynamodb/migration/
|
data/lib/dynomite/migration.rb
CHANGED
|
@@ -1,27 +1,70 @@
|
|
|
1
1
|
module Dynomite
|
|
2
2
|
class Migration
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
include Item::WaiterMethods
|
|
4
|
+
include Dynomite::Client
|
|
5
|
+
include Helpers
|
|
6
6
|
|
|
7
7
|
def up
|
|
8
8
|
puts "Should defined an up method for your migration: #{self.class.name}"
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def create_table(table_name, &block)
|
|
12
|
-
|
|
12
|
+
execute(table_name, :create_table, &block)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def update_table(table_name, &block)
|
|
16
|
-
|
|
16
|
+
execute(table_name, :update_table, &block)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
def delete_table(table_name, &block)
|
|
20
|
+
execute(table_name, :delete_table, &block)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def add_gsi(table_name, *args)
|
|
24
|
+
update_table(table_name) do |t|
|
|
25
|
+
t.add_gsi(*args)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def remove_gsi(table_name, *args)
|
|
30
|
+
update_table(table_name) do |t|
|
|
31
|
+
t.remove_gsi(*args)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def update_gsi(table_name, *args)
|
|
36
|
+
update_table(table_name) do |t|
|
|
37
|
+
t.update_gsi(*args)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def update_time_to_live(table_name, time_to_live_specification={})
|
|
42
|
+
table_name = table_name_with_namespace(table_name)
|
|
43
|
+
client.update_time_to_live(
|
|
44
|
+
table_name: table_name,
|
|
45
|
+
time_to_live_specification: time_to_live_specification
|
|
46
|
+
)
|
|
47
|
+
# fast enough of an operation, no need to wait
|
|
48
|
+
end
|
|
49
|
+
alias update_ttl update_time_to_live
|
|
50
|
+
|
|
19
51
|
private
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
52
|
+
# execute with dsl params
|
|
53
|
+
# table name is short name and params[:table_name] is full namespaced name
|
|
54
|
+
def execute(table_name, method_name, &block)
|
|
55
|
+
params = Dsl.new(method_name, table_name, &block).params
|
|
56
|
+
|
|
57
|
+
puts "Running #{method_name} with:"
|
|
58
|
+
pp params
|
|
59
|
+
resp = client.send(method_name, params)
|
|
60
|
+
namespaced_table_name = params[:table_name] # full: demo-dev_posts short: posts
|
|
61
|
+
puts "DynamoDB Table: #{namespaced_table_name} Status: #{resp.table_description.table_status}"
|
|
62
|
+
|
|
63
|
+
if method_name.to_sym == :delete_table # already sym, to_sym just in case
|
|
64
|
+
waiter.wait_for_delete(params[:table_name])
|
|
65
|
+
else # create_table or update_table
|
|
66
|
+
waiter.wait(params[:table_name])
|
|
67
|
+
end
|
|
25
68
|
end
|
|
26
69
|
end
|
|
27
70
|
end
|
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
module Dynomite
|
|
2
2
|
RESERVED_WORDS = %w[
|
|
3
3
|
as_json
|
|
4
|
-
attrs
|
|
5
4
|
attributes
|
|
6
|
-
|
|
5
|
+
attrs
|
|
7
6
|
columns
|
|
7
|
+
destroy
|
|
8
|
+
fields
|
|
8
9
|
find
|
|
9
10
|
getter
|
|
11
|
+
hash_key
|
|
12
|
+
hash_key_field
|
|
13
|
+
key_schema
|
|
10
14
|
new_record
|
|
11
15
|
param_name
|
|
12
16
|
partition_key
|
|
17
|
+
partition_key_field
|
|
18
|
+
range_key
|
|
19
|
+
range_key_field
|
|
13
20
|
replace
|
|
14
|
-
|
|
21
|
+
save
|
|
15
22
|
scan
|
|
23
|
+
setter
|
|
24
|
+
sort_key_field
|
|
25
|
+
sort_key_field_field
|
|
16
26
|
table_name
|
|
17
27
|
].freeze
|
|
18
28
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Dynomite
|
|
2
|
+
module Types
|
|
3
|
+
TYPE_MAP = {
|
|
4
|
+
string: 'S',
|
|
5
|
+
number: 'N',
|
|
6
|
+
binary: 'B',
|
|
7
|
+
boolean: 'BOOL',
|
|
8
|
+
null: 'NULL',
|
|
9
|
+
map: 'M',
|
|
10
|
+
list: 'L',
|
|
11
|
+
string_set: 'SS',
|
|
12
|
+
number_set: 'NS',
|
|
13
|
+
binary_set: 'BS',
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
# https://rubyonjets.com/docs/database/dynamodb/types/
|
|
17
|
+
# https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypeDescriptors
|
|
18
|
+
def type_map(attribute_type)
|
|
19
|
+
TYPE_MAP[attribute_type.to_s.downcase.to_sym] || attribute_type
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
data/lib/dynomite/version.rb
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Dynomite
|
|
2
|
+
class Waiter
|
|
3
|
+
include Client
|
|
4
|
+
|
|
5
|
+
def wait(table_name)
|
|
6
|
+
logger.info "Waiting for #{table_name} table to be ready."
|
|
7
|
+
statuses = [:initial]
|
|
8
|
+
until statuses.all? { |s| s == "ACTIVE" } do
|
|
9
|
+
resp = client.describe_table(table_name: table_name)
|
|
10
|
+
|
|
11
|
+
table = resp.table
|
|
12
|
+
# table_status: CREATING UPDATING DELETING ACTIVE INACCESSIBLE_ENCRYPTION_CREDENTIALS ARCHIVING ARCHIVED
|
|
13
|
+
table_status = table.table_status
|
|
14
|
+
statuses = [table_status]
|
|
15
|
+
# index_status: CREATING UPDATING DELETING ACTIVE
|
|
16
|
+
indexes = table.global_secondary_indexes || []
|
|
17
|
+
statuses += indexes.map { |i| i.index_status }
|
|
18
|
+
print '.'
|
|
19
|
+
sleep pause_time
|
|
20
|
+
end
|
|
21
|
+
puts
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def wait_for_delete(table_name)
|
|
25
|
+
logger.info "Waiting for #{table_name} table to be deleted."
|
|
26
|
+
begin
|
|
27
|
+
client.describe_table(table_name: table_name)
|
|
28
|
+
print '.'
|
|
29
|
+
sleep pause_time
|
|
30
|
+
rescue Aws::DynamoDB::Errors::ResourceNotFoundException => e
|
|
31
|
+
end
|
|
32
|
+
puts
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
def pause_time
|
|
37
|
+
1
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
data/lib/dynomite.rb
CHANGED
|
@@ -1,25 +1,19 @@
|
|
|
1
1
|
$:.unshift(File.expand_path("../", __FILE__))
|
|
2
|
+
require "active_support"
|
|
3
|
+
require "active_support/concern"
|
|
4
|
+
require "active_support/core_ext/hash"
|
|
5
|
+
require "active_support/core_ext/string"
|
|
2
6
|
require "dynomite/version"
|
|
7
|
+
require "memoist"
|
|
3
8
|
require "rainbow/ext/string"
|
|
4
9
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
'string' => 'S',
|
|
8
|
-
'number' => 'N',
|
|
9
|
-
'binary' => 'B',
|
|
10
|
-
's' => 'S',
|
|
11
|
-
'n' => 'N',
|
|
12
|
-
'b' => 'B',
|
|
13
|
-
}
|
|
10
|
+
require "dynomite/autoloader"
|
|
11
|
+
Dynomite::Autoloader.setup
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
autoload :Dsl, "dynomite/dsl"
|
|
17
|
-
autoload :DbConfig, "dynomite/db_config"
|
|
18
|
-
autoload :Item, "dynomite/item"
|
|
19
|
-
autoload :Core, "dynomite/core"
|
|
20
|
-
autoload :Erb, "dynomite/erb"
|
|
21
|
-
autoload :Log, "dynomite/log"
|
|
22
|
-
autoload :Errors, "dynomite/errors"
|
|
13
|
+
require "dynomite/reserved_words"
|
|
23
14
|
|
|
15
|
+
module Dynomite
|
|
24
16
|
extend Core
|
|
25
17
|
end
|
|
18
|
+
|
|
19
|
+
require "dynomite/engine" if defined?(Jets)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails/generators'
|
|
4
|
+
|
|
5
|
+
# Note: Currently have to use the Rails namespace to allow the Rails generator lookup to work.
|
|
6
|
+
# Would like to figure how to use Dynomite as the namespace instead
|
|
7
|
+
# Usage:
|
|
8
|
+
# jets generate application_item
|
|
9
|
+
module Rails
|
|
10
|
+
module Generators
|
|
11
|
+
class ApplicationItemGenerator < Rails::Generators::Base
|
|
12
|
+
source_root File.expand_path("templates", __dir__)
|
|
13
|
+
|
|
14
|
+
# FIXME: Change this file to a symlink once RubyGems 2.5.0 is required.
|
|
15
|
+
def create_application_item
|
|
16
|
+
template "application_item.rb", application_item_file_name
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
def application_item_file_name
|
|
21
|
+
@application_item_file_name ||=
|
|
22
|
+
if namespaced?
|
|
23
|
+
"app/models/#{namespaced_path}/application_item.rb"
|
|
24
|
+
else
|
|
25
|
+
"app/models/application_item.rb"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require "dynomite"
|
|
2
|
+
|
|
3
|
+
module Jets::Command
|
|
4
|
+
class Dynamodb < Base
|
|
5
|
+
desc "migrate", "Runs migrations"
|
|
6
|
+
long_desc Help.text('dynamodb:migrate')
|
|
7
|
+
def migrate
|
|
8
|
+
Jets.boot
|
|
9
|
+
Dynomite::Migration::Runner.new(options).run
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
desc "generate NAME", "Creates a migration for a DynamoDB table"
|
|
13
|
+
long_desc Help.text('dynamodb:generate')
|
|
14
|
+
option :action, desc: "create_table, update_table, delete_table. Defaults to convention based on the name of the migration."
|
|
15
|
+
option :partition_key, default: "id", desc: "table's partition key"
|
|
16
|
+
option :sort_key, default: nil, desc: "table's sort key"
|
|
17
|
+
option :table_name, desc: "override the the conventional table name"
|
|
18
|
+
def generate(name)
|
|
19
|
+
Dynomite::Migration::Generator.new(name, options).generate
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
desc "seed", "Seed data"
|
|
23
|
+
long_desc Help.text('dynamodb:seed')
|
|
24
|
+
def seed
|
|
25
|
+
Jets.boot
|
|
26
|
+
Dynomite::Seed.new(options).run
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
## Examples
|
|
2
|
+
|
|
3
|
+
jets dynamodb:generate create_products --partition-key category --sort-key product_id:number
|
|
4
|
+
jets dynamodb:generate create_comments --partition-key post_id:string --sort-key created_at:string
|
|
5
|
+
jets dynamodb:generate create_posts --partition-key id # default attribute type is string
|
|
6
|
+
jets dynamodb:generate create_posts --partition-key id:number # attribute type will be number
|
|
7
|
+
|
|
8
|
+
## Running migrations
|
|
9
|
+
|
|
10
|
+
$ jets dynamodb:migrate
|
|
11
|
+
|
|
12
|
+
To add global secondary indexes:
|
|
13
|
+
|
|
14
|
+
$ jets dynamodb:generate update_comments --partition-key user_id:string --sort-key created_at:string
|
|
15
|
+
|
|
16
|
+
To run:
|
|
17
|
+
|
|
18
|
+
$ jets dynamodb:migrate
|
|
19
|
+
|
|
20
|
+
## Conventions
|
|
21
|
+
|
|
22
|
+
A create_table or update_table migration file is generated based name you provide. If `update` is included in the name then an update_table migration table is generated. If `create` is included in the name then a create_table migration table is generated.
|
|
23
|
+
|
|
24
|
+
The table_name is also inferred from the migration name you provide. Examples:
|
|
25
|
+
|
|
26
|
+
$ jets dynamodb:generate create_posts # table_name: posts
|
|
27
|
+
$ jets dynamodb:generate update_comments # table_name: comments
|
|
28
|
+
|
|
29
|
+
You can override both of these conventions:
|
|
30
|
+
|
|
31
|
+
$ jets dynamodb:generate create_my_posts --table-name posts
|
|
32
|
+
$ jets dynamodb:generate my_posts --action create_table --table-name posts
|
|
33
|
+
$ jets dynamodb:generate my_posts --action update_table --table-name posts
|