dynomite 1.2.6 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +17 -2
  3. data/CHANGELOG.md +24 -0
  4. data/Gemfile +1 -5
  5. data/LICENSE.txt +22 -0
  6. data/README.md +6 -188
  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 -299
  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/query.rb +48 -0
  103. data/lib/dynomite/reserved_words.rb +13 -3
  104. data/lib/dynomite/seed.rb +12 -0
  105. data/lib/dynomite/types.rb +22 -0
  106. data/lib/dynomite/version.rb +1 -1
  107. data/lib/dynomite/waiter.rb +40 -0
  108. data/lib/dynomite.rb +11 -17
  109. data/lib/generators/application_item/application_item_generator.rb +30 -0
  110. data/lib/generators/application_item/templates/application_item.rb.tt +4 -0
  111. data/lib/jets/commands/dynamodb_command.rb +29 -0
  112. data/lib/jets/commands/help/generate.md +33 -0
  113. data/lib/jets/commands/help/migrate.md +3 -0
  114. metadata +202 -17
  115. data/docs/migrations/long-example.rb +0 -127
  116. data/docs/migrations/short-example.rb +0 -40
  117. data/lib/dynomite/db_config.rb +0 -107
  118. data/lib/dynomite/errors.rb +0 -15
  119. data/lib/dynomite/log.rb +0 -15
  120. data/lib/dynomite/migration/common.rb +0 -86
  121. data/lib/dynomite/migration/dsl/base_secondary_index.rb +0 -73
  122. data/lib/dynomite/migration/dsl/global_secondary_index.rb +0 -4
  123. data/lib/dynomite/migration/dsl/local_secondary_index.rb +0 -8
  124. 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