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
data/lib/dynomite/cli.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
module Dynomite
|
2
|
+
class CLI < Command
|
3
|
+
desc "migrate", "Run migrations"
|
4
|
+
long_desc Help.text(:migrate)
|
5
|
+
def migrate
|
6
|
+
Migration::Runner.new(options).run
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "generate [name]", "Creates a migration for a DynamoDB table"
|
10
|
+
long_desc Help.text('generate')
|
11
|
+
option :action, desc: "create_table or update_table. Defaults to convention based on the name of the migration."
|
12
|
+
option :partition_key, default: "id:string", desc: "table's partition key"
|
13
|
+
option :sort_key, default: nil, desc: "table's sort key"
|
14
|
+
option :table_name, desc: "override the the conventional table name"
|
15
|
+
def generate(name)
|
16
|
+
Migration::Generator.new(name, options).generate
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "seed", "Seed data"
|
20
|
+
long_desc Help.text(:seed)
|
21
|
+
def seed
|
22
|
+
Seed.new.run
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "install", "Install Dynomite"
|
26
|
+
long_desc Help.text(:install)
|
27
|
+
def install
|
28
|
+
Install.new.run
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "completion *PARAMS", "Prints words for auto-completion."
|
32
|
+
long_desc Help.text(:completion)
|
33
|
+
def completion(*params)
|
34
|
+
Completer.new(CLI, *params).run
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "completion_script", "Generates a script that can be eval to setup auto-completion."
|
38
|
+
long_desc Help.text(:completion_script)
|
39
|
+
def completion_script
|
40
|
+
Completer::Script.generate
|
41
|
+
end
|
42
|
+
|
43
|
+
desc "version", "prints version"
|
44
|
+
def version
|
45
|
+
puts VERSION
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require "aws-sdk-dynamodb"
|
2
|
+
require "erb"
|
3
|
+
require "fileutils"
|
4
|
+
require "json"
|
5
|
+
require "yaml"
|
6
|
+
|
7
|
+
module Dynomite
|
8
|
+
module Client
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
delegate :client, :desc_table, :show_request, :show_response,
|
11
|
+
:warn_scan, :log_debug, :logger,
|
12
|
+
to: :class
|
13
|
+
|
14
|
+
class_methods do
|
15
|
+
extend Memoist
|
16
|
+
|
17
|
+
@@client = nil
|
18
|
+
def client
|
19
|
+
return @@client if @@client
|
20
|
+
|
21
|
+
endpoint = Dynomite.config.endpoint
|
22
|
+
check_dynamodb_local!(endpoint)
|
23
|
+
|
24
|
+
# Normally, do not set the endpoint to use the current configured region.
|
25
|
+
# Probably want to stay in the same region anyway for db connections.
|
26
|
+
#
|
27
|
+
# List of regional endpoints: https://docs.aws.amazon.com/general/latest/gr/rande.html#ddb_region
|
28
|
+
# Example:
|
29
|
+
# endpoint: https://dynamodb.us-east-1.amazonaws.com
|
30
|
+
options = endpoint ? { endpoint: endpoint } : {}
|
31
|
+
log_level = Dynomite.config.log_level.to_s
|
32
|
+
# https://aws.amazon.com/blogs/developer/logging-requests/
|
33
|
+
# https://github.com/aws/aws-sdk-ruby/blob/249a0b34d0014dda50ecc8a09cd58e75e64b3ea4/gems/aws-sdk-core/lib/aws-sdk-core/log/formatter.rb#L212
|
34
|
+
if log_level == "debug"
|
35
|
+
options[:logger] = Dynomite.logger
|
36
|
+
options[:log_formatter] = Aws::Log::Formatter.colored # default short colored
|
37
|
+
end
|
38
|
+
@@client ||= Aws::DynamoDB::Client.new(options)
|
39
|
+
end
|
40
|
+
|
41
|
+
# useful for specs
|
42
|
+
def client=(client)
|
43
|
+
@@client = client
|
44
|
+
end
|
45
|
+
|
46
|
+
def desc_table(table_name)
|
47
|
+
client.describe_table(table_name: table_name).table
|
48
|
+
end
|
49
|
+
memoize :desc_table
|
50
|
+
|
51
|
+
def show_request(params)
|
52
|
+
logger.info("REQUEST: #{JSON.dump(params)}")
|
53
|
+
end
|
54
|
+
|
55
|
+
def show_response(resp)
|
56
|
+
logger.info("RESPONSE: #{JSON.dump(resp)}")
|
57
|
+
end
|
58
|
+
|
59
|
+
# When endoint has been configured to point at dynamodb local: localhost:8000
|
60
|
+
# check if port 8000 is listening and timeout quickly. Or else it takes a
|
61
|
+
# for DynamoDB local to time out, about 10 seconds...
|
62
|
+
# This wastes less of the users time.
|
63
|
+
def check_dynamodb_local!(endpoint)
|
64
|
+
return unless endpoint && endpoint.include?("8000")
|
65
|
+
|
66
|
+
open = port_open?("127.0.0.1", 8000, 0.2)
|
67
|
+
unless open
|
68
|
+
raise "You have configured your app to use DynamoDB local, but it is not running. Please start DynamoDB local. Example: brew cask install dynamodb-local && dynamodb-local"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Thanks: https://gist.github.com/ashrithr/5305786
|
73
|
+
def port_open?(ip, port, seconds=1)
|
74
|
+
# => checks if a port is open or not
|
75
|
+
Timeout::timeout(seconds) do
|
76
|
+
begin
|
77
|
+
TCPSocket.new(ip, port).close
|
78
|
+
true
|
79
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, SocketError
|
80
|
+
false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
rescue Timeout::Error
|
84
|
+
false
|
85
|
+
end
|
86
|
+
|
87
|
+
def warn_scan(message, show: nil)
|
88
|
+
warn_on_scan = show.nil? ? Dynomite.config.warn_on_scan : show
|
89
|
+
return unless warn_on_scan
|
90
|
+
message += <<~EOL
|
91
|
+
You can disable this warning by setting Dynomite.config.warn_on_scan: false
|
92
|
+
EOL
|
93
|
+
logger.info(message)
|
94
|
+
logger.info("Called from: #{call_line}") if call_line
|
95
|
+
end
|
96
|
+
|
97
|
+
def call_line
|
98
|
+
caller.find { |l| l.include?(Dir.pwd) }
|
99
|
+
end
|
100
|
+
|
101
|
+
def log_debug(params)
|
102
|
+
return unless ENV['DYNOMITE_DEBUG']
|
103
|
+
|
104
|
+
call_location = caller_locations[1].to_s # IE: dynomite/item/query/relation.rb:62:in `block in raw_pages'
|
105
|
+
return if call_location.blank? # edge cases
|
106
|
+
method_name = call_location.split('`').last.split(" ").last # IE: raw_pages'
|
107
|
+
method_name.gsub!("'", "") # IE: raw_pages
|
108
|
+
|
109
|
+
logger.info "#{self}##{method_name}" # IE: Dynomite::Item::Query::Relation#raw_pages
|
110
|
+
pp params
|
111
|
+
end
|
112
|
+
|
113
|
+
def logger
|
114
|
+
Dynomite.logger
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require "thor"
|
2
|
+
|
3
|
+
# Override thor's long_desc identation behavior
|
4
|
+
# https://github.com/erikhuda/thor/issues/398
|
5
|
+
class Thor
|
6
|
+
module Shell
|
7
|
+
class Basic
|
8
|
+
def print_wrapped(message, options = {})
|
9
|
+
message = "\n#{message}" unless message[0] == "\n"
|
10
|
+
stdout.puts message
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Dynomite
|
17
|
+
class Command < Thor
|
18
|
+
class << self
|
19
|
+
def dispatch(m, args, options, config)
|
20
|
+
# Allow calling for help via:
|
21
|
+
# dynomite command help
|
22
|
+
# dynomite command -h
|
23
|
+
# dynomite command --help
|
24
|
+
# dynomite command -D
|
25
|
+
#
|
26
|
+
# as well thor's normal way:
|
27
|
+
#
|
28
|
+
# dynomite help command
|
29
|
+
help_flags = Thor::HELP_MAPPINGS + ["help"]
|
30
|
+
if args.length > 1 && !(args & help_flags).empty?
|
31
|
+
args -= help_flags
|
32
|
+
args.insert(-2, "help")
|
33
|
+
end
|
34
|
+
|
35
|
+
# dynomite version
|
36
|
+
# dynomite --version
|
37
|
+
# dynomite -v
|
38
|
+
version_flags = ["--version", "-v"]
|
39
|
+
if args.length == 1 && !(args & version_flags).empty?
|
40
|
+
args = ["version"]
|
41
|
+
end
|
42
|
+
|
43
|
+
super
|
44
|
+
end
|
45
|
+
|
46
|
+
# Override command_help to include the description at the top of the
|
47
|
+
# long_description.
|
48
|
+
def command_help(shell, command_name)
|
49
|
+
meth = normalize_command_name(command_name)
|
50
|
+
command = all_commands[meth]
|
51
|
+
alter_command_description(command)
|
52
|
+
super
|
53
|
+
end
|
54
|
+
|
55
|
+
def alter_command_description(command)
|
56
|
+
return unless command
|
57
|
+
|
58
|
+
# Add description to beginning of long_description
|
59
|
+
long_desc = if command.long_description
|
60
|
+
"#{command.description}\n\n#{command.long_description}"
|
61
|
+
else
|
62
|
+
command.description
|
63
|
+
end
|
64
|
+
|
65
|
+
# add reference url to end of the long_description
|
66
|
+
unless website.empty?
|
67
|
+
full_command = [command.ancestor_name, command.name].compact.join('-')
|
68
|
+
url = "#{website}/reference/dynomite-#{full_command}"
|
69
|
+
long_desc += "\n\nHelp also available at: #{url}"
|
70
|
+
end
|
71
|
+
|
72
|
+
command.long_description = long_desc
|
73
|
+
end
|
74
|
+
private :alter_command_description
|
75
|
+
|
76
|
+
# meant to be overriden
|
77
|
+
def website
|
78
|
+
""
|
79
|
+
end
|
80
|
+
|
81
|
+
# https://github.com/erikhuda/thor/issues/244
|
82
|
+
# Deprecation warning: Thor exit with status 0 on errors. To keep this behavior, you must define `exit_on_failure?` in `Lono::CLI`
|
83
|
+
# You can silence deprecations warning by setting the environment variable THOR_SILENCE_DEPRECATION.
|
84
|
+
def exit_on_failure?
|
85
|
+
true
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
_dynomite() {
|
2
|
+
COMPREPLY=()
|
3
|
+
local word="${COMP_WORDS[COMP_CWORD]}"
|
4
|
+
local words=("${COMP_WORDS[@]}")
|
5
|
+
unset words[0]
|
6
|
+
local completion=$(dynomite completion ${words[@]})
|
7
|
+
COMPREPLY=( $(compgen -W "$completion" -- "$word") )
|
8
|
+
}
|
9
|
+
|
10
|
+
complete -F _dynomite dynomite
|
@@ -0,0 +1,159 @@
|
|
1
|
+
=begin
|
2
|
+
Code Explanation:
|
3
|
+
|
4
|
+
There are 3 types of things to auto-complete:
|
5
|
+
|
6
|
+
1. command: the command itself
|
7
|
+
2. parameters: command parameters.
|
8
|
+
3. options: command options
|
9
|
+
|
10
|
+
Here's an example:
|
11
|
+
|
12
|
+
mycli hello name --from me
|
13
|
+
|
14
|
+
* command: hello
|
15
|
+
* parameters: name
|
16
|
+
* option: --from
|
17
|
+
|
18
|
+
When command parameters are done processing, the remaining completion words will be options. We can tell that the command params are completed based on the method arity.
|
19
|
+
|
20
|
+
## Arity
|
21
|
+
|
22
|
+
For example, say you had a method for a CLI command with the following form:
|
23
|
+
|
24
|
+
ufo scale service count --cluster development
|
25
|
+
|
26
|
+
It's equivalent ruby method:
|
27
|
+
|
28
|
+
scale(service, count) = has an arity of 2
|
29
|
+
|
30
|
+
So typing:
|
31
|
+
|
32
|
+
ufo scale service count [TAB] # there are 3 parameters including the "scale" command according to Thor's CLI processing.
|
33
|
+
|
34
|
+
So the completion should only show options, something like this:
|
35
|
+
|
36
|
+
--noop --verbose --cluster
|
37
|
+
|
38
|
+
## Splat Arguments
|
39
|
+
|
40
|
+
When the ruby method has a splat argument, it's arity is negative. Here are some example methods and their arities.
|
41
|
+
|
42
|
+
ship(service) = 1
|
43
|
+
scale(service, count) = 2
|
44
|
+
ships(*services) = -1
|
45
|
+
foo(example, *rest) = -2
|
46
|
+
|
47
|
+
Fortunately, negative and positive arity values are processed the same way. So we take simply take the absolute value of the arity and process it the same.
|
48
|
+
|
49
|
+
Here are some test cases, hit TAB after typing the command:
|
50
|
+
|
51
|
+
dynomite completion
|
52
|
+
dynomite completion hello
|
53
|
+
dynomite completion hello name
|
54
|
+
dynomite completion hello name --
|
55
|
+
dynomite completion hello name --noop
|
56
|
+
|
57
|
+
dynomite completion
|
58
|
+
dynomite completion sub:goodbye
|
59
|
+
dynomite completion sub:goodbye name
|
60
|
+
|
61
|
+
## Subcommands and Thor::Group Registered Commands
|
62
|
+
|
63
|
+
Sometimes the commands are not simple thor commands but are subcommands or Thor::Group commands. A good specific example is the ufo tool.
|
64
|
+
|
65
|
+
* regular command: ufo ship
|
66
|
+
* subcommand: ufo docker
|
67
|
+
* Thor::Group command: ufo init
|
68
|
+
|
69
|
+
Auto-completion accounts for each of these type of commands.
|
70
|
+
=end
|
71
|
+
module Dynomite
|
72
|
+
class Completer
|
73
|
+
def initialize(command_class, *params)
|
74
|
+
@params = params
|
75
|
+
@current_command = @params[0]
|
76
|
+
@command_class = command_class # CLI initiall
|
77
|
+
end
|
78
|
+
|
79
|
+
def run
|
80
|
+
if subcommand?(@current_command)
|
81
|
+
subcommand_class = @command_class.subcommand_classes[@current_command]
|
82
|
+
@params.shift # destructive
|
83
|
+
Completer.new(subcommand_class, *@params).run # recursively use subcommand
|
84
|
+
return
|
85
|
+
end
|
86
|
+
|
87
|
+
# full command has been found!
|
88
|
+
unless found?(@current_command)
|
89
|
+
puts all_commands
|
90
|
+
return
|
91
|
+
end
|
92
|
+
|
93
|
+
# will only get to here if command aws found (above)
|
94
|
+
arity = @command_class.instance_method(@current_command).arity.abs
|
95
|
+
if @params.size > arity or thor_group_command?
|
96
|
+
puts options_completion
|
97
|
+
else
|
98
|
+
puts params_completion
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def subcommand?(command)
|
103
|
+
@command_class.subcommands.include?(command)
|
104
|
+
end
|
105
|
+
|
106
|
+
# hacky way to detect that command is a registered Thor::Group command
|
107
|
+
def thor_group_command?
|
108
|
+
command_params(raw=true) == [[:rest, :args]]
|
109
|
+
end
|
110
|
+
|
111
|
+
def found?(command)
|
112
|
+
public_methods = @command_class.public_instance_methods(false)
|
113
|
+
command && public_methods.include?(command.to_sym)
|
114
|
+
end
|
115
|
+
|
116
|
+
# all top-level commands
|
117
|
+
def all_commands
|
118
|
+
commands = @command_class.all_commands.reject do |k,v|
|
119
|
+
v.is_a?(Thor::HiddenCommand)
|
120
|
+
end
|
121
|
+
commands.keys
|
122
|
+
end
|
123
|
+
|
124
|
+
def command_params(raw=false)
|
125
|
+
params = @command_class.instance_method(@current_command).parameters
|
126
|
+
# Example:
|
127
|
+
# >> Sub.instance_method(:goodbye).parameters
|
128
|
+
# => [[:req, :name]]
|
129
|
+
# >>
|
130
|
+
raw ? params : params.map!(&:last)
|
131
|
+
end
|
132
|
+
|
133
|
+
def params_completion
|
134
|
+
offset = @params.size - 1
|
135
|
+
offset_params = command_params[offset..-1]
|
136
|
+
command_params[offset..-1].first
|
137
|
+
end
|
138
|
+
|
139
|
+
def options_completion
|
140
|
+
used = ARGV.select { |a| a.include?('--') } # so we can remove used options
|
141
|
+
|
142
|
+
method_options = @command_class.all_commands[@current_command].options.keys
|
143
|
+
class_options = @command_class.class_options.keys
|
144
|
+
|
145
|
+
all_options = method_options + class_options + ['help']
|
146
|
+
|
147
|
+
all_options.map! { |o| "--#{o.to_s.gsub('_','-')}" }
|
148
|
+
filtered_options = all_options - used
|
149
|
+
filtered_options.uniq
|
150
|
+
end
|
151
|
+
|
152
|
+
# Useful for debugging. Using puts messes up completion.
|
153
|
+
def log(msg)
|
154
|
+
File.open("/tmp/complete.log", "a") do |file|
|
155
|
+
file.puts(msg)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Dynomite
|
2
|
+
class Config
|
3
|
+
attr_accessor :logger, :log_level, :namespace_separator, :endpoint, :env,
|
4
|
+
:default_count_method, :warn_on_scan, :default_namespace, :discover_fields,
|
5
|
+
:migration, :undeclared_field_behavior, :default_field_type, :update_strategy
|
6
|
+
def initialize
|
7
|
+
@logger = Logger.new($stderr)
|
8
|
+
@logger.formatter = ActiveSupport::Logger::SimpleFormatter.new
|
9
|
+
@log_level = nil
|
10
|
+
@namespace_separator = "_"
|
11
|
+
@endpoint = ENV['DYNOMITE_ENDPOINT'] # allow to use local dynamodb
|
12
|
+
@env = ActiveSupport::StringInquirer.new(ENV['DYNOMITE_ENV'] || "development")
|
13
|
+
@default_count_method = :count # slow but accurate. :item_count is faster but can be stale by 6 hours
|
14
|
+
@warn_on_scan = true
|
15
|
+
@discover_fields = false
|
16
|
+
@migration = ActiveSupport::OrderedOptions.new
|
17
|
+
@undeclared_field_behavior = :warn # warn silent error allow
|
18
|
+
# Not implemented: :datetime, :date, :float, :array, :set, :map
|
19
|
+
# as we aws-sdk-dynamodb handles it via :infer
|
20
|
+
@default_field_type = :infer # :string, :integer, :boolean, :time, :infer
|
21
|
+
@update_strategy = :put_item # :put_item, :update_item
|
22
|
+
end
|
23
|
+
|
24
|
+
# User should use namespace. The default_namespace is only used internally so Jets can set it.
|
25
|
+
# Makes it easy to set the namespace from the Jets project namespace.
|
26
|
+
# Example:
|
27
|
+
#
|
28
|
+
# config/initializers/dynomite.rb
|
29
|
+
#
|
30
|
+
# Dynomite.configure do |config|
|
31
|
+
# config.namespace = Jets.project_namespace # IE: demo-dev
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
attr_writer :namespace
|
35
|
+
def namespace
|
36
|
+
ENV['DYNOMITE_NAMESPACE'] || @namespace || @default_namespace || 'dynomite'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/dynomite/core.rb
CHANGED
@@ -1,25 +1,24 @@
|
|
1
1
|
require 'logger'
|
2
2
|
|
3
|
-
module Dynomite
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
@@app_root = ENV['APP_ROOT'] || ENV['JETS_ROOT'] || ENV['RAILS_ROOT']
|
11
|
-
@@app_root = '.' if @@app_root.nil? || @app_root == ''
|
12
|
-
@@app_root = "#{@@app_root}/" unless @@app_root.ends_with?('/')
|
13
|
-
@@app_root
|
14
|
-
end
|
3
|
+
module Dynomite
|
4
|
+
module Core
|
5
|
+
@@root = nil
|
6
|
+
def root
|
7
|
+
return @@root if @@root
|
8
|
+
@@root = ENV['DYNOMITE_ROOT'] || ENV['JETS_ROOT'] || ENV['RAILS_ROOT'] || '.'
|
9
|
+
end
|
15
10
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
11
|
+
@@config = nil
|
12
|
+
def config
|
13
|
+
@@config ||= Config.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def logger
|
17
|
+
config.logger
|
18
|
+
end
|
21
19
|
|
22
|
-
|
23
|
-
|
20
|
+
def configure
|
21
|
+
yield(config)
|
22
|
+
end
|
24
23
|
end
|
25
24
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Dynomite
|
2
|
+
class Engine < ::Jets::Engine
|
3
|
+
config.after_initialize do
|
4
|
+
Dynomite.config.default_namespace = Jets.project_namespace # IE: demo-dev
|
5
|
+
Dynomite.config.migration.deletion_protection_enabled = Jets.env.production?
|
6
|
+
|
7
|
+
# Discover all the fields for all the models from attribute_definitions
|
8
|
+
# and create field methods. Has to be done after_initialize because
|
9
|
+
# need model names for the table_name.
|
10
|
+
quiet_dynamodb_logging do
|
11
|
+
Dynomite::Item.descendants.each do |klass|
|
12
|
+
klass.discover_fields!
|
13
|
+
end if Dynomite.config.discover_fields
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.default_log_level
|
18
|
+
# Note: On AWS Lambda, ARGV[0] is nil
|
19
|
+
if ARGV[0]&.include?("dynamodb") # IE: dynamodb:migrate dynamodb:seed
|
20
|
+
:info
|
21
|
+
else
|
22
|
+
Jets.env.development? ? :debug : :info
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.quiet_dynamodb_logging
|
27
|
+
if ENV['DYNOMITE_DEBUG']
|
28
|
+
# If in debug mode, then leave the log level alone which is debug in development
|
29
|
+
# This shows the describe_table calls on jets console bootup
|
30
|
+
Dynomite.config.log_level = default_log_level
|
31
|
+
else
|
32
|
+
# Otherwise, set the log level to info temporarily to quiet the describe_table calls
|
33
|
+
# Then reset the log level back to the user's configured log level.
|
34
|
+
user_log_level = Dynomite.config.log_level
|
35
|
+
Dynomite.config.log_level = :info
|
36
|
+
end
|
37
|
+
|
38
|
+
yield
|
39
|
+
|
40
|
+
Dynomite::Item.client = nil # reset client. Need to reset the client since it's cached
|
41
|
+
# Go back to the user's configured log level or the default log level if user did not set it.
|
42
|
+
Dynomite.config.log_level = user_log_level || default_log_level
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/dynomite/erb.rb
CHANGED
@@ -7,14 +7,12 @@ require 'erb'
|
|
7
7
|
# result = Dynomite::Erb.result(path, key1: "val1", key2: "val2")
|
8
8
|
#
|
9
9
|
class Dynomite::Erb
|
10
|
-
include Dynomite::Log
|
11
|
-
|
12
10
|
class << self
|
13
11
|
def result(path, variables={})
|
14
12
|
set_template_variables(variables)
|
15
13
|
template = IO.read(path)
|
16
14
|
begin
|
17
|
-
ERB.new(template,
|
15
|
+
ERB.new(template, trim_mode: '-').result(binding)
|
18
16
|
rescue Exception => e
|
19
17
|
log(e)
|
20
18
|
log(e.backtrace) if ENV['DEBUG']
|
@@ -49,5 +47,9 @@ class Dynomite::Erb
|
|
49
47
|
instance_variable_set(:"@#{key}", value)
|
50
48
|
end
|
51
49
|
end
|
50
|
+
|
51
|
+
def log(msg)
|
52
|
+
logger.info(msg)
|
53
|
+
end
|
52
54
|
end
|
53
55
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Dynomite
|
2
|
+
class Error < StandardError
|
3
|
+
class InvalidPut < Error; end
|
4
|
+
class PrimaryKeyChangedError < Error; end
|
5
|
+
class RecordNotFound < Error; end
|
6
|
+
class RecordNotUnique < Error; end
|
7
|
+
class ReservedWord < Error; end
|
8
|
+
class StaleObject < Error; end
|
9
|
+
class UndeclaredFields < Error; end
|
10
|
+
class Validation < Error; end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
## Examples
|
2
|
+
|
3
|
+
dynomite completion
|
4
|
+
|
5
|
+
Prints words for TAB auto-completion.
|
6
|
+
|
7
|
+
dynomite completion
|
8
|
+
dynomite completion hello
|
9
|
+
dynomite completion hello name
|
10
|
+
|
11
|
+
To enable, TAB auto-completion add the following to your profile:
|
12
|
+
|
13
|
+
eval $(dynomite completion_script)
|
14
|
+
|
15
|
+
Auto-completion example usage:
|
16
|
+
|
17
|
+
dynomite [TAB]
|
18
|
+
dynomite hello [TAB]
|
19
|
+
dynomite hello name [TAB]
|
20
|
+
dynomite hello name --[TAB]
|