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
@@ -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,6 @@
1
+ class Dynomite::Completer::Script
2
+ def self.generate
3
+ bash_script = File.expand_path("script.sh", File.dirname(__FILE__))
4
+ puts "source #{bash_script}"
5
+ end
6
+ 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::Core
4
- # Ensures trailing slash
5
- # Useful for appending a './' in front of a path or leaving it alone.
6
- # Returns: '/path/with/trailing/slash/' or './'
7
- @@app_root = nil
8
- def app_root
9
- return @@app_root if @@app_root
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
- @@logger = nil
17
- def logger
18
- return @@logger if @@logger
19
- @@logger = Logger.new($stderr)
20
- end
11
+ @@config = nil
12
+ def config
13
+ @@config ||= Config.new
14
+ end
15
+
16
+ def logger
17
+ config.logger
18
+ end
21
19
 
22
- def logger=(value)
23
- @@logger = value
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, nil, "-").result(binding)
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]
@@ -0,0 +1,3 @@
1
+ To use, add the following to your `~/.bashrc` or `~/.profile`
2
+
3
+ eval $(dynomite completion_script)
@@ -0,0 +1,3 @@
1
+ ## Examples
2
+
3
+ dynomite migrate
@@ -0,0 +1,9 @@
1
+ module Dynomite::Help
2
+ class << self
3
+ def text(namespaced_command)
4
+ path = namespaced_command.to_s.gsub(':','/')
5
+ path = File.expand_path("../help/#{path}.md", __FILE__)
6
+ IO.read(path) if File.exist?(path)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ module Dynomite
2
+ class Install
3
+ end
4
+ end
@@ -0,0 +1,15 @@
1
+ class Dynomite::Item
2
+ module Abstract
3
+ extend ActiveSupport::Concern
4
+
5
+ class_methods do
6
+ def abstract?
7
+ !!@abstract
8
+ end
9
+
10
+ def abstract!
11
+ @abstract = true
12
+ end
13
+ end
14
+ end
15
+ end