dynomite 1.2.7 → 2.0.0

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.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +17 -2
  3. data/CHANGELOG.md +18 -0
  4. data/Gemfile +1 -5
  5. data/LICENSE.txt +22 -0
  6. data/README.md +6 -190
  7. data/Rakefile +13 -1
  8. data/dynomite.gemspec +9 -2
  9. data/exe/dynomite +14 -0
  10. data/lib/dynomite/associations/association.rb +126 -0
  11. data/lib/dynomite/associations/belongs_to.rb +35 -0
  12. data/lib/dynomite/associations/has_and_belongs_to_many.rb +19 -0
  13. data/lib/dynomite/associations/has_many.rb +19 -0
  14. data/lib/dynomite/associations/has_one.rb +19 -0
  15. data/lib/dynomite/associations/many_association.rb +257 -0
  16. data/lib/dynomite/associations/single_association.rb +157 -0
  17. data/lib/dynomite/associations.rb +248 -0
  18. data/lib/dynomite/autoloader.rb +25 -0
  19. data/lib/dynomite/cli.rb +48 -0
  20. data/lib/dynomite/client.rb +118 -0
  21. data/lib/dynomite/command.rb +89 -0
  22. data/lib/dynomite/completer/script.rb +6 -0
  23. data/lib/dynomite/completer/script.sh +10 -0
  24. data/lib/dynomite/completer.rb +159 -0
  25. data/lib/dynomite/config.rb +39 -0
  26. data/lib/dynomite/core.rb +18 -19
  27. data/lib/dynomite/engine.rb +45 -0
  28. data/lib/dynomite/erb.rb +5 -3
  29. data/lib/dynomite/error.rb +12 -0
  30. data/lib/dynomite/help/completion.md +20 -0
  31. data/lib/dynomite/help/completion_script.md +3 -0
  32. data/lib/dynomite/help/migrate.md +3 -0
  33. data/lib/dynomite/help.rb +9 -0
  34. data/lib/dynomite/install.rb +4 -0
  35. data/lib/dynomite/item/abstract.rb +15 -0
  36. data/lib/dynomite/item/components.rb +33 -0
  37. data/lib/dynomite/item/dsl.rb +101 -0
  38. data/lib/dynomite/item/id.rb +41 -0
  39. data/lib/dynomite/item/indexes/finder.rb +58 -0
  40. data/lib/dynomite/item/indexes/index.rb +21 -0
  41. data/lib/dynomite/item/indexes/primary_index.rb +18 -0
  42. data/lib/dynomite/item/indexes.rb +25 -0
  43. data/lib/dynomite/item/locking.rb +53 -0
  44. data/lib/dynomite/item/magic_fields.rb +66 -0
  45. data/lib/dynomite/item/primary_key.rb +85 -0
  46. data/lib/dynomite/item/query/delegates.rb +28 -0
  47. data/lib/dynomite/item/query/params/base.rb +42 -0
  48. data/lib/dynomite/item/query/params/expression_attribute.rb +79 -0
  49. data/lib/dynomite/item/query/params/filter.rb +41 -0
  50. data/lib/dynomite/item/query/params/function/attribute_exists.rb +21 -0
  51. data/lib/dynomite/item/query/params/function/attribute_type.rb +30 -0
  52. data/lib/dynomite/item/query/params/function/base.rb +33 -0
  53. data/lib/dynomite/item/query/params/function/begins_with.rb +32 -0
  54. data/lib/dynomite/item/query/params/function/contains.rb +7 -0
  55. data/lib/dynomite/item/query/params/function/size_fn.rb +37 -0
  56. data/lib/dynomite/item/query/params/helpers.rb +94 -0
  57. data/lib/dynomite/item/query/params/key_condition.rb +34 -0
  58. data/lib/dynomite/item/query/params.rb +115 -0
  59. data/lib/dynomite/item/query/partiql/executer.rb +72 -0
  60. data/lib/dynomite/item/query/partiql.rb +67 -0
  61. data/lib/dynomite/item/query/relation/chain.rb +125 -0
  62. data/lib/dynomite/item/query/relation/comparision_expression.rb +21 -0
  63. data/lib/dynomite/item/query/relation/comparision_map.rb +19 -0
  64. data/lib/dynomite/item/query/relation/delete.rb +38 -0
  65. data/lib/dynomite/item/query/relation/ids.rb +21 -0
  66. data/lib/dynomite/item/query/relation/math.rb +19 -0
  67. data/lib/dynomite/item/query/relation/where_field.rb +32 -0
  68. data/lib/dynomite/item/query/relation/where_group.rb +78 -0
  69. data/lib/dynomite/item/query/relation.rb +127 -0
  70. data/lib/dynomite/item/query.rb +7 -0
  71. data/lib/dynomite/item/read/find.rb +196 -0
  72. data/lib/dynomite/item/read/find_with_event.rb +42 -0
  73. data/lib/dynomite/item/read.rb +90 -0
  74. data/lib/dynomite/item/sti.rb +43 -0
  75. data/lib/dynomite/item/table_namespace.rb +43 -0
  76. data/lib/dynomite/item/typecaster.rb +106 -0
  77. data/lib/dynomite/item/waiter_methods.rb +18 -0
  78. data/lib/dynomite/item/write/base.rb +15 -0
  79. data/lib/dynomite/item/write/delete_item.rb +14 -0
  80. data/lib/dynomite/item/write/put_item.rb +99 -0
  81. data/lib/dynomite/item/write/update_item.rb +73 -0
  82. data/lib/dynomite/item/write.rb +204 -0
  83. data/lib/dynomite/item.rb +113 -286
  84. data/lib/dynomite/migration/dsl/accessor.rb +19 -0
  85. data/lib/dynomite/migration/dsl/index/base.rb +42 -0
  86. data/lib/dynomite/migration/dsl/index/gsi.rb +59 -0
  87. data/lib/dynomite/migration/dsl/index/lsi.rb +27 -0
  88. data/lib/dynomite/migration/dsl/index.rb +72 -0
  89. data/lib/dynomite/migration/dsl/primary_key.rb +62 -0
  90. data/lib/dynomite/migration/dsl/provisioned_throughput.rb +38 -0
  91. data/lib/dynomite/migration/dsl.rb +89 -142
  92. data/lib/dynomite/migration/file_info.rb +28 -0
  93. data/lib/dynomite/migration/generator.rb +30 -16
  94. data/lib/dynomite/migration/helpers.rb +7 -0
  95. data/lib/dynomite/migration/internal/migrate/create_schema_migrations.rb +17 -0
  96. data/lib/dynomite/migration/internal/models/schema_migration.rb +6 -0
  97. data/lib/dynomite/migration/runner.rb +178 -0
  98. data/lib/dynomite/migration/templates/create_table.rb +7 -23
  99. data/lib/dynomite/migration/templates/delete_table.rb +7 -0
  100. data/lib/dynomite/migration/templates/update_table.rb +3 -18
  101. data/lib/dynomite/migration.rb +53 -10
  102. data/lib/dynomite/reserved_words.rb +13 -3
  103. data/lib/dynomite/seed.rb +12 -0
  104. data/lib/dynomite/types.rb +22 -0
  105. data/lib/dynomite/version.rb +1 -1
  106. data/lib/dynomite/waiter.rb +40 -0
  107. data/lib/dynomite.rb +11 -17
  108. data/lib/generators/application_item/application_item_generator.rb +30 -0
  109. data/lib/generators/application_item/templates/application_item.rb.tt +4 -0
  110. data/lib/jets/commands/dynamodb_command.rb +29 -0
  111. data/lib/jets/commands/help/generate.md +33 -0
  112. data/lib/jets/commands/help/migrate.md +3 -0
  113. metadata +201 -17
  114. data/docs/migrations/long-example.rb +0 -127
  115. data/docs/migrations/short-example.rb +0 -40
  116. data/lib/dynomite/db_config.rb +0 -121
  117. data/lib/dynomite/errors.rb +0 -15
  118. data/lib/dynomite/log.rb +0 -15
  119. data/lib/dynomite/migration/common.rb +0 -86
  120. data/lib/dynomite/migration/dsl/base_secondary_index.rb +0 -73
  121. data/lib/dynomite/migration/dsl/global_secondary_index.rb +0 -4
  122. data/lib/dynomite/migration/dsl/local_secondary_index.rb +0 -8
  123. 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 for #{@table_name}" unless @options[:quiet]
16
- return if @options[:noop]
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
- puts "Migration file created: #{migration_path}. \nTo run:"
24
- puts " jets dynamodb:migrate #{migration_path}"
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/#{table_action}.rb", __FILE__)
29
- result = Dynomite::Erb.result(path,
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 table_action
39
- @options[:table_action] || conventional_table_action
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 conventional_table_action
43
- @migration_name.include?("update") ? "update_table" : "create_table"
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}_migration".classify # doesnt include timestamp
71
+ "#{@migration_name}".camelize # doesnt include timestamp
58
72
  end
59
73
 
60
74
  def migration_path
61
- "#{Dynomite.app_root}dynamodb/migrate/#{timestamp}-#{@migration_name}_migration.rb"
75
+ "#{Dynomite.root}/dynamodb/migrate/#{timestamp}-#{@migration_name}.rb"
62
76
  end
63
77
 
64
78
  def timestamp
@@ -0,0 +1,7 @@
1
+ class Dynomite::Migration
2
+ module Helpers
3
+ def table_name_with_namespace(table_name)
4
+ [Dynomite.config.namespace, table_name].reject {|s| s.nil? || s.empty?}.join(Dynomite.config.namespace_separator)
5
+ end
6
+ end
7
+ end
@@ -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,6 @@
1
+ class SchemaMigration < Dynomite::Item
2
+ disable_id!
3
+ field :version, type: :integer
4
+ field :time_took, type: :integer
5
+ fields :status, :path, :error_message
6
+ 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 "<%= @partition_key %>" # required
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
- t.billing_mode(:PROVISIONED)
9
- t.provisioned_throughput(<%= @provisioned_throughput %>) # sets both read and write, defaults to 5 when not set
10
-
11
- # Instead of using partition_key and sort_key you can set the
12
- # key schema directly also
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://github.com/tongueroo/dynomite/tree/master/docs
17
+ # More examples: https://rubyonjets.com/docs/database/dynamodb/migration/
@@ -0,0 +1,7 @@
1
+ class <%= @migration_class_name %> < Dynomite::Migration
2
+ def up
3
+ delete_table :<%= @table_name %>
4
+ end
5
+ end
6
+
7
+ # 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.gsi(:create) do |i|
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
- i.provisioned_throughput(5)
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://github.com/tongueroo/dynomite/tree/master/docs
11
+ # More examples: https://rubyonjets.com/docs/database/dynamodb/migration/
@@ -1,27 +1,70 @@
1
1
  module Dynomite
2
2
  class Migration
3
- autoload :Dsl, "dynomite/migration/dsl"
4
- autoload :Generator, "dynomite/migration/generator"
5
- autoload :Executor, "dynomite/migration/executor"
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
- execute_with_dsl_params(table_name, :create_table, &block)
12
+ execute(table_name, :create_table, &block)
13
13
  end
14
14
 
15
15
  def update_table(table_name, &block)
16
- execute_with_dsl_params(table_name, :update_table, &block)
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
- def execute_with_dsl_params(table_name, method_name, &block)
21
- dsl = Dsl.new(method_name, table_name, &block)
22
- params = dsl.params
23
- executor = Executor.new(table_name, method_name, params)
24
- executor.run
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
- delete
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
- setter
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,12 @@
1
+ module Dynomite
2
+ class Seed
3
+ def initialize(options={})
4
+ @options = options
5
+ end
6
+
7
+ def run
8
+ file = "dynamodb/seeds.rb"
9
+ load(file) if File.exist?(file)
10
+ end
11
+ end
12
+ 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
@@ -1,3 +1,3 @@
1
1
  module Dynomite
2
- VERSION = "1.2.7"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -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
- module Dynomite
6
- ATTRIBUTE_TYPES = {
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
- autoload :Migration, "dynomite/migration"
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,4 @@
1
+ <% module_namespacing do -%>
2
+ class ApplicationItem < Dynomite::Item
3
+ end
4
+ <% end -%>