dyna 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +6 -0
- data/bin/dyna +191 -0
- data/dyna.gemspec +32 -0
- data/lib/dyna/client.rb +89 -0
- data/lib/dyna/dsl/converter.rb +87 -0
- data/lib/dyna/dsl/dynamo_db.rb +31 -0
- data/lib/dyna/dsl/table.rb +112 -0
- data/lib/dyna/dsl.rb +54 -0
- data/lib/dyna/exporter.rb +105 -0
- data/lib/dyna/ext/hash-ext.rb +12 -0
- data/lib/dyna/ext/string-ext.rb +25 -0
- data/lib/dyna/filterable.rb +21 -0
- data/lib/dyna/logger.rb +30 -0
- data/lib/dyna/template_helper.rb +20 -0
- data/lib/dyna/utils.rb +17 -0
- data/lib/dyna/version.rb +3 -0
- data/lib/dyna/wrapper/dynamo_db_wrapper.rb +31 -0
- data/lib/dyna/wrapper/table.rb +205 -0
- data/lib/dyna.rb +27 -0
- metadata +181 -0
data/lib/dyna/dsl.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
module Dyna
|
2
|
+
class DSL
|
3
|
+
include Dyna::TemplateHelper
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def define(source, path)
|
7
|
+
self.new(path) do
|
8
|
+
eval(source, binding, path)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def convert(region, exported)
|
13
|
+
Converter.convert(region, exported)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :result
|
18
|
+
|
19
|
+
def initialize(path, &block)
|
20
|
+
@path = path
|
21
|
+
@result = OpenStruct.new(:ddbs => {})
|
22
|
+
|
23
|
+
@context = Hashie::Mash.new(
|
24
|
+
:path => path,
|
25
|
+
:templates => {},
|
26
|
+
)
|
27
|
+
|
28
|
+
instance_eval(&block)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def template(name, &block)
|
33
|
+
@context.templates[name.to_s] = block
|
34
|
+
end
|
35
|
+
|
36
|
+
def require(file)
|
37
|
+
tablefile = (file =~ %r|\A/|) ? file : File.expand_path(File.join(File.dirname(@path), file))
|
38
|
+
|
39
|
+
if File.exist?(tablefile)
|
40
|
+
instance_eval(File.read(tablefile), tablefile)
|
41
|
+
elsif File.exist?(tablefile + '.rb')
|
42
|
+
instance_eval(File.read(tablefile + '.rb'), tablefile + '.rb')
|
43
|
+
else
|
44
|
+
Kernel.require(file)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def dynamo_db(region, &block)
|
49
|
+
ddb = @result.ddbs[region]
|
50
|
+
tables = ddb ? ddb.tables : []
|
51
|
+
@result.ddbs[region] = DynamoDB.new(@context, tables, &block).result
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Dyna
|
2
|
+
class Exporter
|
3
|
+
include Filterable
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def export(ddb, options = {})
|
7
|
+
self.new(ddb, options).export
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(ddb, options = {})
|
12
|
+
@ddb = ddb
|
13
|
+
@options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
def export
|
17
|
+
@ddb.list_tables.table_names
|
18
|
+
.reject { |name| should_skip(name) }
|
19
|
+
.sort
|
20
|
+
.each_with_object({}) do |table_name, result|
|
21
|
+
result[table_name] = self.class.export_table(@ddb, table_name)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.table_definition(describe_table)
|
26
|
+
{
|
27
|
+
table_name: describe_table.table_name,
|
28
|
+
key_schema: key_schema(describe_table),
|
29
|
+
attribute_definitions: attribute_definitions(describe_table),
|
30
|
+
provisioned_throughput: {
|
31
|
+
read_capacity_units: describe_table.provisioned_throughput.read_capacity_units,
|
32
|
+
write_capacity_units: describe_table.provisioned_throughput.write_capacity_units,
|
33
|
+
},
|
34
|
+
local_secondary_indexes: local_secondary_indexes(describe_table),
|
35
|
+
global_secondary_indexes: global_secondary_indexes(describe_table),
|
36
|
+
stream_specification: stream_specification(describe_table),
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def self.export_table(ddb, table_name)
|
42
|
+
describe_table = ddb.describe_table(table_name: table_name).table
|
43
|
+
table_definition(describe_table)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.key_schema(table)
|
47
|
+
table.key_schema.map do |schema|
|
48
|
+
{
|
49
|
+
attribute_name: schema.attribute_name,
|
50
|
+
key_type: schema.key_type,
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.attribute_definitions(table)
|
56
|
+
table.attribute_definitions.map do |definition|
|
57
|
+
{
|
58
|
+
attribute_name: definition.attribute_name,
|
59
|
+
attribute_type: definition.attribute_type,
|
60
|
+
}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.global_secondary_indexes(table)
|
65
|
+
return nil unless table.global_secondary_indexes
|
66
|
+
table.global_secondary_indexes.map do |index|
|
67
|
+
{
|
68
|
+
index_name: index.index_name,
|
69
|
+
key_schema: key_schema(index),
|
70
|
+
projection: {
|
71
|
+
projection_type: index.projection.projection_type,
|
72
|
+
non_key_attributes: index.projection.non_key_attributes,
|
73
|
+
},
|
74
|
+
provisioned_throughput: {
|
75
|
+
read_capacity_units: index.provisioned_throughput.read_capacity_units,
|
76
|
+
write_capacity_units: index.provisioned_throughput.write_capacity_units,
|
77
|
+
},
|
78
|
+
}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.local_secondary_indexes(table)
|
83
|
+
return nil unless table.local_secondary_indexes
|
84
|
+
table.local_secondary_indexes.map do |index|
|
85
|
+
{
|
86
|
+
index_name: index.index_name,
|
87
|
+
key_schema: key_schema(index),
|
88
|
+
projection: {
|
89
|
+
projection_type: index.projection.projection_type,
|
90
|
+
non_key_attributes: index.projection.non_key_attributes,
|
91
|
+
},
|
92
|
+
}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.stream_specification(table)
|
97
|
+
stream_spec = table.stream_specification
|
98
|
+
return nil unless stream_spec
|
99
|
+
{
|
100
|
+
stream_enabled: stream_spec.stream_enabled,
|
101
|
+
stream_view_type: stream_spec.stream_view_type,
|
102
|
+
}
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class Hash
|
2
|
+
def symbolize_keys
|
3
|
+
self.each_with_object({}) do |(k, v), h|
|
4
|
+
h[k.to_s.to_sym] = (v.is_a?(Hash) ? v.symbolize_keys : v)
|
5
|
+
if v.is_a?(Array)
|
6
|
+
h[k.to_s.to_sym] = v.each_with_object([]) do |h2, a|
|
7
|
+
a << (h2.is_a?(Hash) ? h2.symbolize_keys : h2)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class String
|
2
|
+
@@colorize = false
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def colorize=(value)
|
6
|
+
@@colorize = value
|
7
|
+
end
|
8
|
+
|
9
|
+
def colorize
|
10
|
+
@@colorize
|
11
|
+
end
|
12
|
+
end # of class methods
|
13
|
+
|
14
|
+
Term::ANSIColor::Attribute.named_attributes.map do |attribute|
|
15
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
16
|
+
def #{attribute.name}
|
17
|
+
if @@colorize
|
18
|
+
Term::ANSIColor.send(#{attribute.name.inspect}, self)
|
19
|
+
else
|
20
|
+
self
|
21
|
+
end
|
22
|
+
end
|
23
|
+
EOS
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Dyna
|
2
|
+
module Filterable
|
3
|
+
def should_skip(table_name)
|
4
|
+
if @options.table_names
|
5
|
+
unless @options.table_names.include?(table_name)
|
6
|
+
log(:debug, "skip table(with tables_names option) #{table_name}")
|
7
|
+
return true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
if @options.exclude_table_names
|
12
|
+
if @options.exclude_table_names.any? {|regex| table_name =~ regex}
|
13
|
+
log(:debug, "skip table(with exclude_tables_names option) #{table_name}")
|
14
|
+
return true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/dyna/logger.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Dyna
|
2
|
+
class Logger < ::Logger
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
super($stdout)
|
7
|
+
|
8
|
+
self.formatter = proc do |severity, datetime, progname, msg|
|
9
|
+
"#{msg}\n"
|
10
|
+
end
|
11
|
+
|
12
|
+
self.level = Logger::INFO
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_debug(value)
|
16
|
+
self.level = value ? Logger::DEBUG : Logger::INFO
|
17
|
+
end
|
18
|
+
|
19
|
+
module ClientHelper
|
20
|
+
def log(level, message, color, log_id = nil)
|
21
|
+
message = "[#{level.to_s.upcase}] #{message}" unless level == :info
|
22
|
+
message << ": #{log_id}" if log_id
|
23
|
+
message << ' (dry-run)' if @options && @options.dry_run
|
24
|
+
logger = (@options && @options.logger) || Dyna::Logger.instance
|
25
|
+
message = message.send(color) if color
|
26
|
+
logger.send(level, message)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Dyna
|
2
|
+
module TemplateHelper
|
3
|
+
def include_template(template_name, context = {})
|
4
|
+
tmplt = @context.templates[template_name.to_s]
|
5
|
+
|
6
|
+
unless tmplt
|
7
|
+
raise "Template `#{template_name}` is not defined"
|
8
|
+
end
|
9
|
+
|
10
|
+
context_orig = @context
|
11
|
+
@context = @context.merge(context)
|
12
|
+
instance_eval(&tmplt)
|
13
|
+
@context = context_orig
|
14
|
+
end
|
15
|
+
|
16
|
+
def context
|
17
|
+
@context
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/dyna/utils.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Dyna
|
2
|
+
class Utils
|
3
|
+
class << self
|
4
|
+
def diff(obj1, obj2, options = {})
|
5
|
+
diffy = Diffy::Diff.new(
|
6
|
+
obj1.pretty_inspect,
|
7
|
+
obj2.pretty_inspect,
|
8
|
+
:diff => '-u'
|
9
|
+
)
|
10
|
+
|
11
|
+
out = diffy.to_s(options[:color] ? :color : :text).gsub(/\s+\z/m, '')
|
12
|
+
out.gsub!(/^/, options[:indent]) if options[:indent]
|
13
|
+
out
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/dyna/version.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Dyna
|
2
|
+
class DynamoDBWrapper
|
3
|
+
include Logger::ClientHelper
|
4
|
+
|
5
|
+
def initialize(ddb, options)
|
6
|
+
@ddb = ddb
|
7
|
+
@options = options.dup
|
8
|
+
end
|
9
|
+
|
10
|
+
def tables
|
11
|
+
@ddb.list_tables.table_names.map do |table_name|
|
12
|
+
describe_table = @ddb.describe_table(table_name: table_name).table
|
13
|
+
Table.new(@ddb, describe_table, @options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def create(dsl)
|
18
|
+
log(:info, 'Create Table', :cyan, "#{dsl.table_name}")
|
19
|
+
|
20
|
+
unless @options.dry_run
|
21
|
+
result = @ddb.create_table(dsl.symbolize_keys)
|
22
|
+
@options.updated = true
|
23
|
+
result
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def updated?
|
28
|
+
!!@options.updated
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
module Dyna
|
2
|
+
class DynamoDBWrapper
|
3
|
+
class Table
|
4
|
+
extend Forwardable
|
5
|
+
include Logger::ClientHelper
|
6
|
+
|
7
|
+
def_delegators(
|
8
|
+
:@table,
|
9
|
+
:table_name
|
10
|
+
)
|
11
|
+
|
12
|
+
def initialize(ddb, table, options)
|
13
|
+
@ddb = ddb
|
14
|
+
@table = table
|
15
|
+
@options = options
|
16
|
+
end
|
17
|
+
|
18
|
+
def eql?(dsl)
|
19
|
+
definition_eql?(dsl)
|
20
|
+
end
|
21
|
+
|
22
|
+
def update(dsl)
|
23
|
+
unless provisioned_throughput_eql?(dsl)
|
24
|
+
wait_until_table_is_active
|
25
|
+
update_table(dsl_provisioned_throughput(dsl))
|
26
|
+
end
|
27
|
+
unless global_secondary_indexes_eql?(dsl)
|
28
|
+
wait_until_table_is_active
|
29
|
+
update_table_index(dsl, dsl_global_secondary_index_updates(dsl))
|
30
|
+
end
|
31
|
+
unless stream_specification_eql?(dsl)
|
32
|
+
wait_until_table_is_active
|
33
|
+
update_stream_specification(dsl_stream_specification(dsl))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete
|
38
|
+
log(:info, 'Delete Table', :red, "#{table_name}")
|
39
|
+
|
40
|
+
unless @options.dry_run
|
41
|
+
@ddb.delete_table(table_name: @table.table_name)
|
42
|
+
@options.updated = true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def definition
|
47
|
+
Exporter.table_definition(@table).symbolize_keys
|
48
|
+
end
|
49
|
+
|
50
|
+
def wait_until_table_is_active
|
51
|
+
log(:info, "waiting table #{@table.table_name} to be ACTIVE or deleted..", false)
|
52
|
+
loop do
|
53
|
+
begin
|
54
|
+
desc = @ddb.describe_table(table_name: table_name).table
|
55
|
+
rescue => e
|
56
|
+
break
|
57
|
+
end
|
58
|
+
status = desc.table_status
|
59
|
+
log(:info, "status... #{status}", false)
|
60
|
+
break if desc.table_status == 'ACTIVE'
|
61
|
+
sleep 3
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def definition_eql?(dsl)
|
67
|
+
definition == dsl.definition
|
68
|
+
end
|
69
|
+
|
70
|
+
def provisioned_throughput_eql?(dsl)
|
71
|
+
self_provisioned_throughput == dsl_provisioned_throughput(dsl)
|
72
|
+
end
|
73
|
+
|
74
|
+
def self_provisioned_throughput
|
75
|
+
definition.select {|k,v| k == :provisioned_throughput}
|
76
|
+
end
|
77
|
+
|
78
|
+
def dsl_provisioned_throughput(dsl)
|
79
|
+
dsl.symbolize_keys.select {|k,v| k == :provisioned_throughput}
|
80
|
+
end
|
81
|
+
|
82
|
+
def global_secondary_indexes_eql?(dsl)
|
83
|
+
self_global_secondary_indexes == dsl_global_secondary_indexes(dsl)
|
84
|
+
end
|
85
|
+
|
86
|
+
def self_global_secondary_indexes
|
87
|
+
definition[:global_secondary_indexes]
|
88
|
+
end
|
89
|
+
|
90
|
+
def dsl_global_secondary_indexes(dsl)
|
91
|
+
dsl.symbolize_keys[:global_secondary_indexes]
|
92
|
+
end
|
93
|
+
|
94
|
+
def dsl_global_secondary_index_updates(dsl)
|
95
|
+
actual_by_name = (self_global_secondary_indexes || {}).group_by { |index| index[:index_name] }.each_with_object({}) do |(k, v), h|
|
96
|
+
h[k] = v.first
|
97
|
+
end
|
98
|
+
expect_by_name = (dsl_global_secondary_indexes(dsl) || {}).group_by { |index| index[:index_name] }.each_with_object({}) do |(k, v), h|
|
99
|
+
h[k] = v.first
|
100
|
+
end
|
101
|
+
params = []
|
102
|
+
expect_by_name.each do |index_name, expect_index|
|
103
|
+
actual_index = actual_by_name[index_name]
|
104
|
+
unless actual_index
|
105
|
+
unless params.empty?
|
106
|
+
log(:warn, 'Can not add multiple GSI at once', :yellow, index_name)
|
107
|
+
next
|
108
|
+
end
|
109
|
+
params << {create: expect_index}
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
expect_by_name.each do |index_name, expect_index|
|
114
|
+
actual_index = actual_by_name.delete(index_name)
|
115
|
+
if actual_index != nil &&
|
116
|
+
actual_index[:provisioned_throughput] != expect_index[:provisioned_throughput]
|
117
|
+
if params.any? { |param| param[:update] }
|
118
|
+
log(:warn, 'Can not update multiple GSI at once', :yellow, index_name)
|
119
|
+
next
|
120
|
+
end
|
121
|
+
params << {update: {
|
122
|
+
index_name: index_name,
|
123
|
+
provisioned_throughput: expect_index[:provisioned_throughput]
|
124
|
+
}}
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
actual_by_name.each do |index_name, actual_index|
|
129
|
+
if params.any? { |param| param[:delete] }
|
130
|
+
log(:warn, 'Can not delete multiple GSI at once', :yellow, index_name)
|
131
|
+
next
|
132
|
+
end
|
133
|
+
params << {delete: { index_name: index_name }}
|
134
|
+
end
|
135
|
+
|
136
|
+
params
|
137
|
+
end
|
138
|
+
|
139
|
+
def stream_specification_eql?(dsl)
|
140
|
+
actual = self_stream_specification
|
141
|
+
expect = dsl_stream_specification(dsl)
|
142
|
+
if (actual == nil || actual[:stream_specification] == nil) &&
|
143
|
+
(expect == nil || expect[:stream_specification] == nil || expect[:stream_specification][:stream_enabled] == false)
|
144
|
+
return true
|
145
|
+
end
|
146
|
+
actual == expect
|
147
|
+
end
|
148
|
+
|
149
|
+
def self_stream_specification
|
150
|
+
definition.select {|k,v| k == :stream_specification}
|
151
|
+
end
|
152
|
+
|
153
|
+
def dsl_stream_specification(dsl)
|
154
|
+
dsl.symbolize_keys.select {|k,v| k == :stream_specification}
|
155
|
+
end
|
156
|
+
|
157
|
+
def update_stream_specification(dsl)
|
158
|
+
log(:info, " table: #{@table.table_name}(update stream spec)\n".green + Dyna::Utils.diff(self_stream_specification, dsl, :color => @options.color, :indent => ' '), false)
|
159
|
+
unless @options.dry_run
|
160
|
+
params = { table_name: @table.table_name }.merge(dsl)
|
161
|
+
@ddb.update_table(params)
|
162
|
+
@options.updated = true
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def update_table(dsl)
|
167
|
+
log(:info, " table: #{@table.table_name}\n".green + Dyna::Utils.diff(self_provisioned_throughput, dsl, :color => @options.color, :indent => ' '), false)
|
168
|
+
unless @options.dry_run
|
169
|
+
params = dsl.dup
|
170
|
+
params[:table_name] = @table.table_name
|
171
|
+
@ddb.update_table(params)
|
172
|
+
@options.updated = true
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def update_table_index(dsl, index_params)
|
177
|
+
log(:info, " table: #{@table.table_name}(update GSI)".green, false)
|
178
|
+
index_params.each do |index_param|
|
179
|
+
if index_param[:create]
|
180
|
+
log(:info, " index: #{index_param[:create][:index_name]}(create GSI)".cyan, false)
|
181
|
+
log(:info, " => #{index_param[:create]}".cyan, false)
|
182
|
+
end
|
183
|
+
if index_param[:update]
|
184
|
+
log(:info, " index: #{index_param[:update][:index_name]}(update GSI)".green, false)
|
185
|
+
log(:info, " => #{index_param[:update]}".green, false)
|
186
|
+
end
|
187
|
+
if index_param[:delete]
|
188
|
+
log(:info, " index: #{index_param[:delete][:index_name]}(delete GSI)".red, false)
|
189
|
+
log(:info, " => #{index_param[:delete]}".red, false)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
unless @options.dry_run
|
194
|
+
params = {
|
195
|
+
table_name: @table.table_name,
|
196
|
+
attribute_definitions: dsl.symbolize_keys[:attribute_definitions],
|
197
|
+
global_secondary_index_updates: index_params,
|
198
|
+
}
|
199
|
+
@ddb.update_table(params)
|
200
|
+
@options.updated = true
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
data/lib/dyna.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'logger'
|
3
|
+
require 'term/ansicolor'
|
4
|
+
require 'diffy'
|
5
|
+
require 'hashie'
|
6
|
+
require 'singleton'
|
7
|
+
|
8
|
+
require 'aws-sdk'
|
9
|
+
|
10
|
+
require 'dyna/version'
|
11
|
+
require 'dyna/logger'
|
12
|
+
require 'dyna/filterable'
|
13
|
+
require 'dyna/client'
|
14
|
+
require 'dyna/exporter'
|
15
|
+
require 'dyna/template_helper'
|
16
|
+
require 'dyna/dsl'
|
17
|
+
require 'dyna/utils'
|
18
|
+
require 'dyna/dsl/converter'
|
19
|
+
require 'dyna/dsl/dynamo_db'
|
20
|
+
require 'dyna/dsl/table'
|
21
|
+
require 'dyna/ext/string-ext'
|
22
|
+
require 'dyna/ext/hash-ext'
|
23
|
+
require 'dyna/wrapper/table'
|
24
|
+
require 'dyna/wrapper/dynamo_db_wrapper'
|
25
|
+
|
26
|
+
module Dyna
|
27
|
+
end
|