dynamodb_model 0.3.0 → 1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +21 -1
- data/docs/{migrations-long.rb → migrations/long-example.rb} +0 -0
- data/docs/{migrations-short.rb → migrations/short-example.rb} +7 -0
- data/lib/dynamodb_model.rb +10 -0
- data/lib/dynamodb_model/erb.rb +51 -0
- data/lib/dynamodb_model/item.rb +1 -1
- data/lib/dynamodb_model/migration/common.rb +4 -2
- data/lib/dynamodb_model/migration/dsl.rb +57 -19
- data/lib/dynamodb_model/migration/dsl/base_secondary_index.rb +72 -0
- data/lib/dynamodb_model/migration/dsl/global_secondary_index.rb +1 -68
- data/lib/dynamodb_model/migration/dsl/local_secondary_index.rb +8 -0
- data/lib/dynamodb_model/migration/generator.rb +38 -18
- data/lib/dynamodb_model/migration/{template.rb → templates/create_table.rb} +2 -29
- data/lib/dynamodb_model/migration/templates/update_table.rb +26 -0
- data/lib/dynamodb_model/util.rb +7 -7
- data/lib/dynamodb_model/version.rb +1 -1
- metadata +9 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 355ffd3eb3d0d42649b31239c6297464be46d260
|
4
|
+
data.tar.gz: e6ec2b09237a89b6e0c617d0e0f888c5578a52a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57b068c88fbf47de849a3980be63a98411f0103bc979c2457248b71e3d17b7d7bec0fc9a45a3f99d7a939ac5f811f0c5ee814907def64105bd678465af5e3721
|
7
|
+
data.tar.gz: c0c1ba569bbc4a1e411b3cc6821abd96a003a0fdba71dc56b6846f30275d3fd309ac8b6f524658b990045c1ec4745fd77880dbed2dc5a57665eecb847a4e0a45
|
data/CHANGELOG.md
CHANGED
@@ -3,6 +3,11 @@
|
|
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.0.0]
|
7
|
+
- LSI support
|
8
|
+
- automatically infer table_name
|
9
|
+
- automatically infer create_table and update_table migrations types
|
10
|
+
|
6
11
|
## [0.3.0]
|
7
12
|
- DSL methods now available: create_table, update_table
|
8
13
|
- Also can add GSI indexes within update_table with: i.gsi
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# DynamodbModel
|
2
2
|
|
3
|
-
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
|
3
|
+
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:
|
4
4
|
|
5
5
|
## Examples
|
6
6
|
|
@@ -69,6 +69,26 @@ posts = Post.scan(options)
|
|
69
69
|
posts # Array of Post items. [Post.new, Post.new, ...]
|
70
70
|
```
|
71
71
|
|
72
|
+
Examples are also in [item_spec.rb](spec/lib/dynamodb_model/item_spec.rb).
|
73
|
+
|
74
|
+
## Migration Support
|
75
|
+
|
76
|
+
DynamodbModel supports ActiveRecord-like migrations. Here's a short example:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
class CreateCommentsMigration < DynamodbModel::Migration
|
80
|
+
def up
|
81
|
+
create_table :comments do |t|
|
82
|
+
t.partition_key "post_id:string" # required
|
83
|
+
t.sort_key "created_at:string" # optional
|
84
|
+
t.provisioned_throughput(5) # sets both read and write, defaults to 5 when not set
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
```
|
89
|
+
|
90
|
+
More examples are in the [docs/migrations](docs/migrations) folder.
|
91
|
+
|
72
92
|
## Installation
|
73
93
|
|
74
94
|
Add this line to your application's Gemfile:
|
File without changes
|
@@ -4,6 +4,13 @@ class CreateCommentsMigration < DynamodbModel::Migration
|
|
4
4
|
t.partition_key "post_id:string" # required
|
5
5
|
t.sort_key "created_at:string" # optional
|
6
6
|
t.provisioned_throughput(5) # sets both read and write, defaults to 5 when not set
|
7
|
+
|
8
|
+
t.lsi do |i|
|
9
|
+
i.partition_key "user_id:string"
|
10
|
+
i.sort_key "updated_at:string" # optional
|
11
|
+
|
12
|
+
i.provisioned_throughput(10)
|
13
|
+
end
|
7
14
|
end
|
8
15
|
end
|
9
16
|
end
|
data/lib/dynamodb_model.rb
CHANGED
@@ -2,11 +2,21 @@ $:.unshift(File.expand_path("../", __FILE__))
|
|
2
2
|
require "dynamodb_model/version"
|
3
3
|
|
4
4
|
module DynamodbModel
|
5
|
+
ATTRIBUTE_TYPES = {
|
6
|
+
'string' => 'S',
|
7
|
+
'number' => 'N',
|
8
|
+
'binary' => 'B',
|
9
|
+
's' => 'S',
|
10
|
+
'n' => 'N',
|
11
|
+
'b' => 'B',
|
12
|
+
}
|
13
|
+
|
5
14
|
autoload :Migration, "dynamodb_model/migration"
|
6
15
|
autoload :Dsl, "dynamodb_model/dsl"
|
7
16
|
autoload :DbConfig, "dynamodb_model/db_config"
|
8
17
|
autoload :Item, "dynamodb_model/item"
|
9
18
|
autoload :Util, "dynamodb_model/util"
|
19
|
+
autoload :Erb, "dynamodb_model/erb"
|
10
20
|
|
11
21
|
extend Util
|
12
22
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
# Renders Erb and provide better backtrace where there's an error
|
4
|
+
#
|
5
|
+
# Usage:
|
6
|
+
#
|
7
|
+
# result = DynamodbModel::Erb.result(path, key1: "val1", key2: "val2")
|
8
|
+
#
|
9
|
+
class DynamodbModel::Erb
|
10
|
+
class << self
|
11
|
+
def result(path, variables={})
|
12
|
+
set_template_variables(variables)
|
13
|
+
template = IO.read(path)
|
14
|
+
begin
|
15
|
+
ERB.new(template, nil, "-").result(binding)
|
16
|
+
rescue Exception => e
|
17
|
+
puts e
|
18
|
+
puts e.backtrace if ENV['DEBUG']
|
19
|
+
|
20
|
+
# how to know where ERB stopped? - https://www.ruby-forum.com/topic/182051
|
21
|
+
# syntax errors have the (erb):xxx info in e.message
|
22
|
+
# undefined variables have (erb):xxx info in e.backtrac
|
23
|
+
error_info = e.message.split("\n").grep(/\(erb\)/)[0]
|
24
|
+
error_info ||= e.backtrace.grep(/\(erb\)/)[0]
|
25
|
+
raise unless error_info # unable to find the (erb):xxx: error line
|
26
|
+
line = error_info.split(':')[1].to_i
|
27
|
+
puts "Error evaluating ERB template on line #{line.to_s.colorize(:red)} of: #{path.sub(/^\.\//, '').colorize(:green)}"
|
28
|
+
|
29
|
+
template_lines = template.split("\n")
|
30
|
+
context = 5 # lines of context
|
31
|
+
top, bottom = [line-context-1, 0].max, line+context-1
|
32
|
+
spacing = template_lines.size.to_s.size
|
33
|
+
template_lines[top..bottom].each_with_index do |line_content, index|
|
34
|
+
line_number = top+index+1
|
35
|
+
if line_number == line
|
36
|
+
printf("%#{spacing}d %s\n".colorize(:red), line_number, line_content)
|
37
|
+
else
|
38
|
+
printf("%#{spacing}d %s\n", line_number, line_content)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
exit 1 unless ENV['TEST']
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_template_variables(variables)
|
46
|
+
variables.each do |key, value|
|
47
|
+
instance_variable_set(:"@#{key}", value)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/dynamodb_model/item.rb
CHANGED
@@ -120,7 +120,7 @@ module DynamodbModel
|
|
120
120
|
#
|
121
121
|
# AWS Docs examples: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GettingStarted.Ruby.04.html
|
122
122
|
def self.scan(params={})
|
123
|
-
puts("
|
123
|
+
puts("It's recommended to not use scan for production. It can be slow and expensive. You can a LSI or GSI and query the index instead.")
|
124
124
|
params = { table_name: table_name }.merge(params)
|
125
125
|
resp = db.scan(params)
|
126
126
|
resp.items.map {|i| self.new(i) }
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# Common methods to the *SecondaryIndex classes that handle gsi and lsi methods
|
2
|
+
# as well a the Dsl class that handles create_table and update_table methods.
|
1
3
|
class DynamodbModel::Migration::Dsl
|
2
4
|
module Common
|
3
5
|
# http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Types/KeySchemaElement.html
|
@@ -31,7 +33,7 @@ class DynamodbModel::Migration::Dsl
|
|
31
33
|
|
32
34
|
attribute_definition = {
|
33
35
|
attribute_name: name,
|
34
|
-
attribute_type:
|
36
|
+
attribute_type: DynamodbModel::ATTRIBUTE_TYPES[attribute_type]
|
35
37
|
}
|
36
38
|
@attribute_definitions << attribute_definition
|
37
39
|
end
|
@@ -73,7 +75,7 @@ class DynamodbModel::Migration::Dsl
|
|
73
75
|
write: :write_capacity_units,
|
74
76
|
}
|
75
77
|
|
76
|
-
if capacity_type
|
78
|
+
if capacity_type == :both
|
77
79
|
@provisioned_throughput[map[:read]] = capacity_units
|
78
80
|
@provisioned_throughput[map[:write]] = capacity_units
|
79
81
|
else
|
@@ -1,20 +1,13 @@
|
|
1
1
|
class DynamodbModel::Migration
|
2
2
|
class Dsl
|
3
|
-
autoload :GlobalSecondaryIndex, "dynamodb_model/migration/dsl/global_secondary_index"
|
4
3
|
autoload :Common, "dynamodb_model/migration/common"
|
4
|
+
autoload :BaseSecondaryIndex, "dynamodb_model/migration/dsl/base_secondary_index"
|
5
|
+
autoload :LocalSecondaryIndex, "dynamodb_model/migration/dsl/local_secondary_index"
|
6
|
+
autoload :GlobalSecondaryIndex, "dynamodb_model/migration/dsl/global_secondary_index"
|
5
7
|
|
6
8
|
include DynamodbModel::DbConfig
|
7
9
|
include Common
|
8
10
|
|
9
|
-
ATTRIBUTE_TYPE_MAP = {
|
10
|
-
'string' => 'S',
|
11
|
-
'number' => 'N',
|
12
|
-
'binary' => 'B',
|
13
|
-
's' => 'S',
|
14
|
-
'n' => 'N',
|
15
|
-
'b' => 'B',
|
16
|
-
}
|
17
|
-
|
18
11
|
attr_accessor :key_schema, :attribute_definitions
|
19
12
|
attr_accessor :table_name
|
20
13
|
def initialize(method_name, table_name, &block)
|
@@ -35,6 +28,7 @@ class DynamodbModel::Migration
|
|
35
28
|
|
36
29
|
# Attributes for update_table only:
|
37
30
|
@gsi_indexes = []
|
31
|
+
@lsi_indexes = []
|
38
32
|
end
|
39
33
|
|
40
34
|
# t.gsi(:create) do |i|
|
@@ -47,6 +41,17 @@ class DynamodbModel::Migration
|
|
47
41
|
end
|
48
42
|
alias_method :global_secondary_index, :gsi
|
49
43
|
|
44
|
+
# t.lsi(:create) do |i|
|
45
|
+
# i.partition_key = "category:string"
|
46
|
+
# i.sort_key = "created_at:string" # optional
|
47
|
+
# end
|
48
|
+
def lsi(action=:create, index_name=nil, &block)
|
49
|
+
# dont need action create but have it to keep the lsi and gsi method consistent
|
50
|
+
lsi_index = LocalSecondaryIndex.new(index_name, &block)
|
51
|
+
@lsi_indexes << lsi_index # store @lsi_index for the parent Dsl to use
|
52
|
+
end
|
53
|
+
alias_method :local_secondary_index, :gsi
|
54
|
+
|
50
55
|
def evaluate
|
51
56
|
return if @evaluated
|
52
57
|
@block.call(self) if @block
|
@@ -69,20 +74,53 @@ class DynamodbModel::Migration
|
|
69
74
|
end
|
70
75
|
|
71
76
|
def params_create_table
|
72
|
-
{
|
77
|
+
params = {
|
73
78
|
table_name: namespaced_table_name,
|
74
79
|
key_schema: @key_schema,
|
75
80
|
attribute_definitions: @attribute_definitions,
|
76
81
|
provisioned_throughput: @provisioned_throughput
|
77
82
|
}
|
83
|
+
|
84
|
+
params[:local_secondary_index_creates] = local_secondary_index_creates unless @lsi_indexes.empty?
|
85
|
+
params
|
86
|
+
end
|
87
|
+
|
88
|
+
# Goes thorugh all the lsi_indexes that have been built up in memory.
|
89
|
+
# Find the lsi object that creates an index and then grab the
|
90
|
+
# attribute_definitions from it.
|
91
|
+
def lsi_create_attribute_definitions
|
92
|
+
lsi = @lsi_indexes.first # DynamoDB only supports adding one index at a time anyway. The reason @lsi_indexes is an Array is because we're sharing the same class code for LSI and GSI
|
93
|
+
if lsi
|
94
|
+
lsi.evaluate # force early evaluate since we need the params to
|
95
|
+
# add: gsi_attribute_definitions + lsi_attrs
|
96
|
+
lsi_attrs = lsi.attribute_definitions
|
97
|
+
end
|
98
|
+
all_attrs = if lsi_attrs
|
99
|
+
@attribute_definitions + lsi_attrs
|
100
|
+
else
|
101
|
+
@attribute_definitions
|
102
|
+
end
|
103
|
+
all_attrs.uniq
|
104
|
+
end
|
105
|
+
|
106
|
+
# maps each lsi to the hash structure expected by dynamodb update_table
|
107
|
+
# under the global_secondary_index_updates key:
|
108
|
+
#
|
109
|
+
# { create: {...} }
|
110
|
+
# { update: {...} }
|
111
|
+
# { delete: {...} }
|
112
|
+
def lsi_secondary_index_creates
|
113
|
+
@lsi_indexes.map do |lsi|
|
114
|
+
{ lsi.action => lsi.params }
|
115
|
+
end
|
78
116
|
end
|
79
117
|
|
80
118
|
def params_update_table
|
81
119
|
params = {
|
82
120
|
table_name: namespaced_table_name,
|
121
|
+
attribute_definitions: gsi_create_attribute_definitions,
|
83
122
|
# update table take values only some values for the "parent" table
|
84
|
-
|
85
|
-
# key_schema: @key_schema, # update_table does not handle key_schema for the "parent" table,
|
123
|
+
# no key_schema, update_table does not handle key_schema for the "parent" table
|
86
124
|
}
|
87
125
|
# only set "parent" table provisioned_throughput if user actually invoked
|
88
126
|
# it in the dsl
|
@@ -98,13 +136,13 @@ class DynamodbModel::Migration
|
|
98
136
|
gsi = @gsi_indexes.find { |gsi| gsi.action == :create }
|
99
137
|
if gsi
|
100
138
|
gsi.evaluate # force early evaluate since we need the params to
|
101
|
-
# add:
|
139
|
+
# add: gsi_attribute_definitions + gsi_attrs
|
102
140
|
gsi_attrs = gsi.attribute_definitions
|
103
141
|
end
|
104
142
|
all_attrs = if gsi_attrs
|
105
|
-
|
143
|
+
gsi_attribute_definitions + gsi_attrs
|
106
144
|
else
|
107
|
-
|
145
|
+
gsi_attribute_definitions
|
108
146
|
end
|
109
147
|
all_attrs.uniq
|
110
148
|
end
|
@@ -124,11 +162,11 @@ class DynamodbModel::Migration
|
|
124
162
|
# >> resp = Post.db.describe_table(table_name: "proj-dev-posts")
|
125
163
|
# >> resp.table.attribute_definitions.map(&:to_h)
|
126
164
|
# => [{:attribute_name=>"id", :attribute_type=>"S"}]
|
127
|
-
def
|
128
|
-
return @
|
165
|
+
def gsi_attribute_definitions
|
166
|
+
return @gsi_attribute_definitions if @gsi_attribute_definitions
|
129
167
|
|
130
168
|
resp = db.describe_table(table_name: namespaced_table_name)
|
131
|
-
@
|
169
|
+
@gsi_attribute_definitions = resp.table.attribute_definitions.map(&:to_h)
|
132
170
|
end
|
133
171
|
end
|
134
172
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# Base class for LocalSecondaryIndex and GlobalSecondaryIndex
|
2
|
+
class DynamodbModel::Migration::Dsl
|
3
|
+
class BaseSecondaryIndex
|
4
|
+
include Common
|
5
|
+
|
6
|
+
attr_accessor :action, :key_schema, :attribute_definitions
|
7
|
+
attr_accessor :index_name
|
8
|
+
def initialize(action, index_name=nil, &block)
|
9
|
+
@action = action.to_sym
|
10
|
+
# for gsi action can be: :create, :update, :delete
|
11
|
+
# for lsi action is always: :create
|
12
|
+
@index_name = index_name
|
13
|
+
@block = block
|
14
|
+
|
15
|
+
# Dsl fills these atttributes in as methods are called within
|
16
|
+
# the block
|
17
|
+
@key_schema = []
|
18
|
+
@attribute_definitions = []
|
19
|
+
# default provisioned_throughput
|
20
|
+
@provisioned_throughput = {
|
21
|
+
read_capacity_units: 5,
|
22
|
+
write_capacity_units: 5
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def index_name
|
27
|
+
@index_name || conventional_index_name
|
28
|
+
end
|
29
|
+
|
30
|
+
def conventional_index_name
|
31
|
+
# @partition_key_identifier and @sort_key_identifier are set as immediately
|
32
|
+
# when the partition_key and sort_key methods are called in the dsl block.
|
33
|
+
# Usually look like this:
|
34
|
+
#
|
35
|
+
# @partition_key_identifier: post_id:string
|
36
|
+
# @sort_key_identifier: updated_at:string
|
37
|
+
#
|
38
|
+
# We strip the :string portion in case it is provided
|
39
|
+
#
|
40
|
+
partition_key = @partition_key_identifier.split(':').first
|
41
|
+
sort_key = @sort_key_identifier.split(':').first if @sort_key_identifier
|
42
|
+
[partition_key, sort_key, "index"].compact.join('-')
|
43
|
+
end
|
44
|
+
|
45
|
+
def evaluate
|
46
|
+
return if @evaluated
|
47
|
+
@block.call(self) if @block
|
48
|
+
@evaluated = true
|
49
|
+
end
|
50
|
+
|
51
|
+
def params
|
52
|
+
evaluate # lazy evaluation: wait until as long as possible before evaluating code block
|
53
|
+
|
54
|
+
params = { index_name: index_name } # required for all actions
|
55
|
+
|
56
|
+
if @action == :create
|
57
|
+
params[:key_schema] = @key_schema # required for create action
|
58
|
+
# hardcode to ALL for now
|
59
|
+
params[:projection] = { # required
|
60
|
+
projection_type: "ALL", # accepts ALL, KEYS_ONLY, INCLUDE
|
61
|
+
# non_key_attributes: ["NonKeyAttributeName"],
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
if [:create, :update].include?(@action)
|
66
|
+
params[:provisioned_throughput] = @provisioned_throughput
|
67
|
+
end
|
68
|
+
|
69
|
+
params
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -1,71 +1,4 @@
|
|
1
1
|
class DynamodbModel::Migration::Dsl
|
2
|
-
class GlobalSecondaryIndex
|
3
|
-
include Common
|
4
|
-
|
5
|
-
ATTRIBUTE_TYPE_MAP = DynamodbModel::Migration::Dsl::ATTRIBUTE_TYPE_MAP
|
6
|
-
|
7
|
-
attr_accessor :action, :key_schema, :attribute_definitions
|
8
|
-
attr_accessor :index_name
|
9
|
-
def initialize(action, index_name=nil, &block)
|
10
|
-
@action = action.to_sym # :create, :update, :index
|
11
|
-
@index_name = index_name
|
12
|
-
@block = block
|
13
|
-
|
14
|
-
# Dsl fills these atttributes in as methods are called within
|
15
|
-
# the block
|
16
|
-
@key_schema = []
|
17
|
-
@attribute_definitions = []
|
18
|
-
# default provisioned_throughput
|
19
|
-
@provisioned_throughput = {
|
20
|
-
read_capacity_units: 5,
|
21
|
-
write_capacity_units: 5
|
22
|
-
}
|
23
|
-
end
|
24
|
-
|
25
|
-
def index_name
|
26
|
-
@index_name || conventional_index_name
|
27
|
-
end
|
28
|
-
|
29
|
-
def conventional_index_name
|
30
|
-
# @partition_key_identifier and @sort_key_identifier are set as immediately
|
31
|
-
# when the partition_key and sort_key methods are called in the dsl block.
|
32
|
-
# Usually look like this:
|
33
|
-
#
|
34
|
-
# @partition_key_identifier: post_id:string
|
35
|
-
# @sort_key_identifier: updated_at:string
|
36
|
-
#
|
37
|
-
# We strip the :string portion in case it is provided
|
38
|
-
#
|
39
|
-
partition_key = @partition_key_identifier.split(':').first
|
40
|
-
sort_key = @sort_key_identifier.split(':').first if @sort_key_identifier
|
41
|
-
[partition_key, sort_key, "index"].compact.join('-')
|
42
|
-
end
|
43
|
-
|
44
|
-
def evaluate
|
45
|
-
return if @evaluated
|
46
|
-
@block.call(self) if @block
|
47
|
-
@evaluated = true
|
48
|
-
end
|
49
|
-
|
50
|
-
def params
|
51
|
-
evaluate # lazy evaluation: wait until as long as possible before evaluating code block
|
52
|
-
|
53
|
-
params = { index_name: index_name } # required for all actions
|
54
|
-
|
55
|
-
if @action == :create
|
56
|
-
params[:key_schema] = @key_schema # required for create action
|
57
|
-
# hardcode to ALL for now
|
58
|
-
params[:projection] = { # required
|
59
|
-
projection_type: "ALL", # accepts ALL, KEYS_ONLY, INCLUDE
|
60
|
-
# non_key_attributes: ["NonKeyAttributeName"],
|
61
|
-
}
|
62
|
-
end
|
63
|
-
|
64
|
-
if [:create, :update].include?(@action)
|
65
|
-
params[:provisioned_throughput] = @provisioned_throughput
|
66
|
-
end
|
67
|
-
|
68
|
-
params
|
69
|
-
end
|
2
|
+
class GlobalSecondaryIndex < BaseSecondaryIndex
|
70
3
|
end
|
71
4
|
end
|
@@ -5,9 +5,9 @@ class DynamodbModel::Migration
|
|
5
5
|
class Generator
|
6
6
|
include DynamodbModel::DbConfig
|
7
7
|
|
8
|
-
attr_reader :table_name
|
9
|
-
def initialize(
|
10
|
-
@
|
8
|
+
attr_reader :migration_name, :table_name
|
9
|
+
def initialize(migration_name, options)
|
10
|
+
@migration_name = migration_name
|
11
11
|
@options = options
|
12
12
|
end
|
13
13
|
|
@@ -18,27 +18,47 @@ class DynamodbModel::Migration
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def create_migration
|
21
|
-
|
22
|
-
migration_path = "#{DynamodbModel.root}#{migration_relative_path}"
|
23
|
-
dir = File.dirname(migration_path)
|
24
|
-
FileUtils.mkdir_p(dir) unless File.exist?(dir)
|
21
|
+
FileUtils.mkdir_p(File.dirname(migration_path))
|
25
22
|
IO.write(migration_path, migration_code)
|
26
|
-
puts "Migration file created: #{
|
27
|
-
puts " jets
|
23
|
+
puts "Migration file created: #{migration_path}. \nTo run:"
|
24
|
+
puts " jets dynamodb migrate #{migration_path}"
|
28
25
|
end
|
29
26
|
|
30
27
|
def migration_code
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
28
|
+
path = File.expand_path("../templates/#{table_action}.rb", __FILE__)
|
29
|
+
result = DynamodbModel::Erb.result(path,
|
30
|
+
migration_class_name: migration_class_name,
|
31
|
+
table_name: table_name,
|
32
|
+
partition_key: @options[:partition_key],
|
33
|
+
sort_key: @options[:sort_key],
|
34
|
+
provisioned_throughput: @options[:provisioned_throughput] || 5,
|
35
|
+
)
|
38
36
|
end
|
39
37
|
|
40
|
-
def
|
41
|
-
|
38
|
+
def table_action
|
39
|
+
@options[:table_action] || conventional_table_action
|
40
|
+
end
|
41
|
+
|
42
|
+
def conventional_table_action
|
43
|
+
@migration_name.include?("update") ? "update_table" : "create_table"
|
44
|
+
end
|
45
|
+
|
46
|
+
def table_name
|
47
|
+
@options[:table_name] || conventional_table_name
|
48
|
+
end
|
49
|
+
|
50
|
+
# create_posts => posts
|
51
|
+
# update_posts => posts
|
52
|
+
def conventional_table_name
|
53
|
+
@migration_name.sub(/^(create|update)_/, '')
|
54
|
+
end
|
55
|
+
|
56
|
+
def migration_class_name
|
57
|
+
"#{@migration_name}_migration".classify # doesnt include timestamp
|
58
|
+
end
|
59
|
+
|
60
|
+
def migration_path
|
61
|
+
"#{DynamodbModel.app_root}dynamodb/migrate/#{timestamp}-#{@migration_name}_migration.rb"
|
42
62
|
end
|
43
63
|
|
44
64
|
def timestamp
|
@@ -1,32 +1,3 @@
|
|
1
|
-
# Note: table name created will be namespaced based on
|
2
|
-
# DynamodbModel::Migration.table_namespace. This can be set in
|
3
|
-
# config/dynamodb.yml
|
4
|
-
#
|
5
|
-
# development:
|
6
|
-
# table_namespace: "mynamespace"
|
7
|
-
#
|
8
|
-
# This results in:
|
9
|
-
# create_table "posts" => table name: "mynamespace-posts"
|
10
|
-
#
|
11
|
-
# When you're in a in Jets project you can set the namespace based on
|
12
|
-
# Jets.config.table_namespace, which is based on the project name and
|
13
|
-
# a short version of the environment. Example:
|
14
|
-
#
|
15
|
-
# `config/dynamodb.yml`:
|
16
|
-
# development:
|
17
|
-
# table_namespace: <%%= Jets.config.table_namespace %>
|
18
|
-
#
|
19
|
-
# If your project_name is proj and environment is production:
|
20
|
-
# create_table "posts" => table name: "proj-prod-posts"
|
21
|
-
#
|
22
|
-
# If your project_name is proj and environment is staging:
|
23
|
-
# create_table "posts" => table name: "proj-stag-posts"
|
24
|
-
#
|
25
|
-
# If your project_name is proj and environment is development:
|
26
|
-
# create_table "posts" => table name: "proj-dev-posts"
|
27
|
-
#
|
28
|
-
# If the table_namespace is set to a blank string or nil, then a namespace
|
29
|
-
# will not be prepended at all.
|
30
1
|
class <%= @migration_class_name %> < DynamodbModel::Migration
|
31
2
|
def up
|
32
3
|
create_table :<%= @table_name %> do |t|
|
@@ -57,3 +28,5 @@ class <%= @migration_class_name %> < DynamodbModel::Migration
|
|
57
28
|
end
|
58
29
|
end
|
59
30
|
end
|
31
|
+
|
32
|
+
# More examples: https://github.com/tongueroo/dynamodb_model/tree/master/docs
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class <%= @migration_class_name %> < DynamodbModel::Migration
|
2
|
+
def up
|
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 -%>
|
9
|
+
|
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.
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# More examples: https://github.com/tongueroo/dynamodb_model/tree/master/docs
|
data/lib/dynamodb_model/util.rb
CHANGED
@@ -4,12 +4,12 @@ module DynamodbModel::Util
|
|
4
4
|
# Ensures trailing slash
|
5
5
|
# Useful for appending a './' in front of a path or leaving it alone.
|
6
6
|
# Returns: '/path/with/trailing/slash/' or './'
|
7
|
-
@@
|
8
|
-
def
|
9
|
-
return @@
|
10
|
-
@@
|
11
|
-
@@
|
12
|
-
@@
|
13
|
-
@@
|
7
|
+
@@app_root = nil
|
8
|
+
def app_root
|
9
|
+
return @@app_root if @@app_root
|
10
|
+
@@app_root = ENV['APP_ROOT'].to_s
|
11
|
+
@@app_root = '.' if @@app_root == ''
|
12
|
+
@@app_root = "#{@@app_root}/" unless @@app_root.ends_with?('/')
|
13
|
+
@@app_root
|
14
14
|
end
|
15
15
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynamodb_model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tung Nguyen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-11-
|
11
|
+
date: 2017-11-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -95,19 +95,23 @@ files:
|
|
95
95
|
- Rakefile
|
96
96
|
- bin/console
|
97
97
|
- bin/setup
|
98
|
-
- docs/migrations-
|
99
|
-
- docs/migrations-
|
98
|
+
- docs/migrations/long-example.rb
|
99
|
+
- docs/migrations/short-example.rb
|
100
100
|
- dynamodb_model.gemspec
|
101
101
|
- lib/dynamodb_model.rb
|
102
102
|
- lib/dynamodb_model/db_config.rb
|
103
|
+
- lib/dynamodb_model/erb.rb
|
103
104
|
- lib/dynamodb_model/item.rb
|
104
105
|
- lib/dynamodb_model/migration.rb
|
105
106
|
- lib/dynamodb_model/migration/common.rb
|
106
107
|
- lib/dynamodb_model/migration/dsl.rb
|
108
|
+
- lib/dynamodb_model/migration/dsl/base_secondary_index.rb
|
107
109
|
- lib/dynamodb_model/migration/dsl/global_secondary_index.rb
|
110
|
+
- lib/dynamodb_model/migration/dsl/local_secondary_index.rb
|
108
111
|
- lib/dynamodb_model/migration/executor.rb
|
109
112
|
- lib/dynamodb_model/migration/generator.rb
|
110
|
-
- lib/dynamodb_model/migration/
|
113
|
+
- lib/dynamodb_model/migration/templates/create_table.rb
|
114
|
+
- lib/dynamodb_model/migration/templates/update_table.rb
|
111
115
|
- lib/dynamodb_model/util.rb
|
112
116
|
- lib/dynamodb_model/version.rb
|
113
117
|
homepage: https://github.com/tongueroo/dynamodb_model
|