dynomite 1.2.7 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +17 -2
- data/CHANGELOG.md +18 -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 +45 -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 +178 -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 +12 -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
@@ -3,8 +3,6 @@ require "active_support/core_ext/string"
|
|
3
3
|
class Dynomite::Migration
|
4
4
|
# jets dynamodb:generate posts --partition-key id:string
|
5
5
|
class Generator
|
6
|
-
include Dynomite::DbConfig
|
7
|
-
|
8
6
|
attr_reader :migration_name, :table_name
|
9
7
|
def initialize(migration_name, options)
|
10
8
|
@migration_name = migration_name
|
@@ -12,21 +10,28 @@ class Dynomite::Migration
|
|
12
10
|
end
|
13
11
|
|
14
12
|
def generate
|
15
|
-
puts "Generating migration
|
16
|
-
return if
|
13
|
+
puts "Generating migration" unless @options[:quiet]
|
14
|
+
return if ENV['NOOP']
|
17
15
|
create_migration
|
18
16
|
end
|
19
17
|
|
20
18
|
def create_migration
|
21
19
|
FileUtils.mkdir_p(File.dirname(migration_path))
|
22
20
|
IO.write(migration_path, migration_code)
|
23
|
-
|
24
|
-
puts "
|
21
|
+
pretty_migration_path = migration_path.sub(/^\.\//,'') # remove leading ./
|
22
|
+
puts "Migration file created: #{pretty_migration_path}\nTo run:\n\n"
|
23
|
+
command = File.basename($0)
|
24
|
+
if command == "jets"
|
25
|
+
puts " #{command} dynamodb:migrate"
|
26
|
+
else
|
27
|
+
puts " #{command} migrate"
|
28
|
+
end
|
29
|
+
puts
|
25
30
|
end
|
26
31
|
|
27
32
|
def migration_code
|
28
|
-
path = File.expand_path("../templates/#{
|
29
|
-
|
33
|
+
path = File.expand_path("../templates/#{action}.rb", __FILE__)
|
34
|
+
Dynomite::Erb.result(path,
|
30
35
|
migration_class_name: migration_class_name,
|
31
36
|
table_name: table_name,
|
32
37
|
partition_key: @options[:partition_key],
|
@@ -35,30 +40,39 @@ class Dynomite::Migration
|
|
35
40
|
)
|
36
41
|
end
|
37
42
|
|
38
|
-
def
|
39
|
-
|
43
|
+
def action
|
44
|
+
# optoins[:table_action] is old and deprecated
|
45
|
+
action = @options[:action] || @options[:table_action] || conventional_action
|
46
|
+
case action
|
47
|
+
when /create/
|
48
|
+
"create_table"
|
49
|
+
when /delete/
|
50
|
+
"delete_table"
|
51
|
+
else
|
52
|
+
"update_table" # default and fallback
|
53
|
+
end
|
40
54
|
end
|
41
55
|
|
42
|
-
def
|
43
|
-
@migration_name.
|
56
|
+
def conventional_action
|
57
|
+
@migration_name.split("_").first
|
44
58
|
end
|
45
59
|
|
46
60
|
def table_name
|
47
|
-
@options[:table_name] || conventional_table_name
|
61
|
+
@options[:table_name] || conventional_table_name || "TABLE_NAME"
|
48
62
|
end
|
49
63
|
|
50
64
|
# create_posts => posts
|
51
65
|
# update_posts => posts
|
52
66
|
def conventional_table_name
|
53
|
-
@migration_name.sub(/^(create|update)_/, '')
|
67
|
+
@migration_name.sub(/^(create|update|delete)_/, '')
|
54
68
|
end
|
55
69
|
|
56
70
|
def migration_class_name
|
57
|
-
"#{@migration_name}
|
71
|
+
"#{@migration_name}".camelize # doesnt include timestamp
|
58
72
|
end
|
59
73
|
|
60
74
|
def migration_path
|
61
|
-
"#{Dynomite.
|
75
|
+
"#{Dynomite.root}/dynamodb/migrate/#{timestamp}-#{@migration_name}.rb"
|
62
76
|
end
|
63
77
|
|
64
78
|
def timestamp
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class CreateSchemaMigrations < Dynomite::Migration
|
2
|
+
include Dynomite::Client
|
3
|
+
|
4
|
+
def up
|
5
|
+
create_table :schema_migrations do |t|
|
6
|
+
t.partition_key "version:number" # required
|
7
|
+
t.billing_mode "PAY_PER_REQUEST"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def table_exist?(full_table_name)
|
12
|
+
client.describe_table(table_name: full_table_name)
|
13
|
+
true
|
14
|
+
rescue Aws::DynamoDB::Errors::ResourceNotFoundException
|
15
|
+
false
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,178 @@
|
|
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
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
puts "Running Dynomite migrations"
|
15
|
+
ensure_schema_migrations_exist!
|
16
|
+
clear_error_schema_migrations! if ENV['CLEAR_ERRORS']
|
17
|
+
check_for_migration_errors!
|
18
|
+
Dynomite::Migration::FileInfo.all_files.each do |path|
|
19
|
+
migrate(path)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def migrate(path)
|
24
|
+
# load migration class definition
|
25
|
+
# CreatePosts#up (up is called below)
|
26
|
+
file_info = FileInfo.new(path)
|
27
|
+
|
28
|
+
schema_migration = SchemaMigration.find_by(version: file_info.version)
|
29
|
+
if schema_migration
|
30
|
+
teriminal_statuses = %w[complete error]
|
31
|
+
if teriminal_statuses.include?(schema_migration.status)
|
32
|
+
return
|
33
|
+
else
|
34
|
+
action = with_timeout(message: "Timed out. You must respond within 60s.") do
|
35
|
+
uncompleted_migration_prompt(file_info, schema_migration)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
case action
|
41
|
+
when :skip
|
42
|
+
return
|
43
|
+
when :delete
|
44
|
+
schema_migration.delete
|
45
|
+
when :completed
|
46
|
+
schema_migration.status = "completed"
|
47
|
+
schema_migration.save
|
48
|
+
return
|
49
|
+
when :exit
|
50
|
+
puts "Exiting"
|
51
|
+
exit
|
52
|
+
end
|
53
|
+
|
54
|
+
puts "Running migration: #{file_info.pretty_path}"
|
55
|
+
load path
|
56
|
+
|
57
|
+
# INSERT schema_migration table - in_progress
|
58
|
+
unless schema_migration
|
59
|
+
schema_migration = SchemaMigration.new(version: file_info.version, status: "in_progress", path: file_info.pretty_path)
|
60
|
+
schema_migration.save
|
61
|
+
end
|
62
|
+
start_time = Time.now
|
63
|
+
|
64
|
+
# Run actual migration
|
65
|
+
migration_class = file_info.migration_class
|
66
|
+
error_message = nil
|
67
|
+
begin
|
68
|
+
# Runs migration up command. Example:
|
69
|
+
# CreatePosts#up
|
70
|
+
# Migration#create_table
|
71
|
+
# Migration#execute (wait happens here)
|
72
|
+
migration_class.new.up # wait happens within create_table or update_table
|
73
|
+
rescue Aws::DynamoDB::Errors::ServiceError => error
|
74
|
+
puts "Unable to #{@method_name.to_s.gsub('_',' ')}: #{error.message}".color(:red)
|
75
|
+
error_message = error.message
|
76
|
+
end
|
77
|
+
|
78
|
+
# UPDATE schema_migrations table - complete status
|
79
|
+
if error_message
|
80
|
+
schema_migration.status = "error"
|
81
|
+
schema_migration.error_message = error_message
|
82
|
+
else
|
83
|
+
schema_migration.status = "complete"
|
84
|
+
end
|
85
|
+
|
86
|
+
schema_migration.time_took = (Time.now - start_time).to_i
|
87
|
+
schema_migration.save
|
88
|
+
# schema_migration.delete # HACK
|
89
|
+
puts "Time took: #{pretty_time_took(schema_migration.time_took)}"
|
90
|
+
exit 1 if error_message # otherwise continue to next migration file
|
91
|
+
end
|
92
|
+
|
93
|
+
def with_timeout(options={}, &block)
|
94
|
+
seconds = options[:seconds] || 60
|
95
|
+
message = options[:message] || "Timed out after #{seconds}s."
|
96
|
+
Timeout::timeout(seconds, &block)
|
97
|
+
rescue Timeout::Error => e
|
98
|
+
puts "#{e.class}: #{e.message}"
|
99
|
+
puts message
|
100
|
+
exit 1
|
101
|
+
end
|
102
|
+
|
103
|
+
def uncompleted_migration_prompt(file_info, schema_migration)
|
104
|
+
choice = nil
|
105
|
+
until %w[s d c e].include?(choice)
|
106
|
+
puts(<<~EOL)
|
107
|
+
The #{file_info.pretty_path} migration status is incomplete. Status: #{schema_migration.status}
|
108
|
+
This can happen and if it was interrupted by a CTRL-C.
|
109
|
+
Please check the migration to help determine what to do next.
|
110
|
+
|
111
|
+
Options:
|
112
|
+
|
113
|
+
s - skip and continue. leaves schema_migrations item as-is
|
114
|
+
d - delete and continue. deletes the schema_migrations item
|
115
|
+
c - mark as successful completed and continue. updates the schema_migrations item as completed.
|
116
|
+
e - exit
|
117
|
+
|
118
|
+
EOL
|
119
|
+
print "Choose an option (s/d/c/e): "
|
120
|
+
choice = $stdin.gets.strip
|
121
|
+
end
|
122
|
+
|
123
|
+
map = {
|
124
|
+
"s" => :skip,
|
125
|
+
"d" => :delete,
|
126
|
+
"c" => :completed,
|
127
|
+
"e" => :exit,
|
128
|
+
}
|
129
|
+
map[choice]
|
130
|
+
end
|
131
|
+
|
132
|
+
def ensure_schema_migrations_exist!
|
133
|
+
migration = CreateSchemaMigrations.new
|
134
|
+
return if migration.table_exist?(SchemaMigration.table_name)
|
135
|
+
puts "Creating #{SchemaMigration.table_name} table for the first time"
|
136
|
+
migration.up
|
137
|
+
end
|
138
|
+
|
139
|
+
def clear_error_schema_migrations!
|
140
|
+
SchemaMigration.warn_on_scan(false).where(status: "error").each do |schema_migration|
|
141
|
+
schema_migration.delete
|
142
|
+
puts "Deleted error schema_migration: #{schema_migration.path}"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def check_for_migration_errors!
|
147
|
+
errors = SchemaMigration.warn_on_scan(false).where(status: "error")
|
148
|
+
errors_info = SchemaMigration.warn_on_scan(false).where(status: "error").map do |schema_migration|
|
149
|
+
" #{schema_migration.path} - #{schema_migration.error_message}"
|
150
|
+
end.join("\n")
|
151
|
+
if errors.count > 0
|
152
|
+
puts <<~EOL
|
153
|
+
Found error migrations. Please review the migration erors fix before continuing.
|
154
|
+
You can clear them out manually by deleting them from the #{SchemaMigration.table_name} table.
|
155
|
+
|
156
|
+
#{errors_info}
|
157
|
+
|
158
|
+
You can also clear them with the following command:
|
159
|
+
|
160
|
+
CLEAR_ERRORS=1 jets dynamodb:migrate
|
161
|
+
|
162
|
+
EOL
|
163
|
+
exit 1
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# http://stackoverflow.com/questions/4175733/convert-duration-to-hoursminutesseconds-or-similar-in-rails-3-or-ruby
|
168
|
+
def pretty_time_took(total_seconds)
|
169
|
+
minutes = (total_seconds / 60) % 60
|
170
|
+
seconds = total_seconds % 60
|
171
|
+
if total_seconds < 60
|
172
|
+
"#{seconds.to_i}s"
|
173
|
+
else
|
174
|
+
"#{minutes.to_i}m #{seconds.to_i}s"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
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
|