dynomite 1.2.4 → 1.2.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 55dd0f93e12c5b18e5ae2d1997be0089e60024d4656e15720732b8d8da743d97
4
- data.tar.gz: d8bf1232ace35602f896691af0e1036a31744adff2dec35b4eea7930c3c65ec6
3
+ metadata.gz: 8a0639976cc739134d6670945d099f3584b39f88f058e056678591a42c09be84
4
+ data.tar.gz: 6ad8c28d9147a175cf940ec756d808b66a948c27be74239ab95dc3b589dd9d74
5
5
  SHA512:
6
- metadata.gz: b16044c3d220c9e9a55634faf04a8d9c79a593377be323fca6a20ca9e7bbe8fa6e5e7c60c4f6bdebede938c420ed319272e62f75926c5937cb878714ba5ab2ea
7
- data.tar.gz: c43283a308192ab948e78b278db6f465d162d291fb06ce56e6fccfbbdfa1225cf3cc98ca9c463a01a85aa83ab4b4f4ca5fffc786fce70410e3da68e32a803e88
6
+ metadata.gz: 3f7f4e2dcb3163c4cc60065c3d0f74b835bd4763f6295ae00d8f92576f2c57afcc969a5b4bf0c727d9b8a45ab17e52ae573319a819cf3f7c0974cf29a91b19ac
7
+ data.tar.gz: c37b011a90f943e6e04fbdfa248dee7b8bcf1dae762ae5175c976e02325e462118715d8340f5d42022e62a40ab1b77aa80af532544e255eed2c2fec89ecd6aa8
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ vendor/bundle
data/CHANGELOG.md CHANGED
@@ -3,8 +3,20 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  This project *tries* to adhere to [Semantic Versioning](http://semver.org/), even before v1.0.
5
5
 
6
+ ## [1.2.7] - 2022-06-12
7
+ - [#23](https://github.com/tongueroo/dynomite/pull/23) #where method refactor to allow Model.index_name('index').where(...)
8
+ - [#24](https://github.com/tongueroo/dynomite/pull/24) Add get_endpoint_ip to db_config.rb
9
+ - [#26](https://github.com/tongueroo/dynomite/pull/26) change pay_per_use to pay_per_request
10
+ - [#27](https://github.com/tongueroo/dynomite/pull/27) Fixed message that tells how to install dynamodb-local
11
+
12
+ ## [1.2.6]
13
+ - Implement the `PAY_PER_USE` billing mode for table creations and updates. See [DynamoDB On Demand](https://aws.amazon.com/blogs/aws/amazon-dynamodb-on-demand-no-capacity-planning-and-pay-per-request-pricing/).
14
+
15
+ ## [1.2.5]
16
+ - use correct color method
17
+
6
18
  ## [1.2.4]
7
- - #16 add rainbow gem dependency for colorize method
19
+ - #16 add rainbow gem dependency for color method
8
20
  - #17 fix table names for models with namespaces
9
21
 
10
22
  ## [1.2.3]
data/README.md CHANGED
@@ -1,9 +1,17 @@
1
1
  # Dynomite
2
2
 
3
- NOTE: Am looking for maintainers to help with this gem. Send me an email! Also [dynamoid](https://github.com/Dynamoid/dynamoid) seems like a good option that should be considered delegating to or straight using. Learning on delegation so we can have better default behavior for a DynamoDB model layer for Jets.
3
+ [![BoltOps Badge](https://img.boltops.com/boltops/badges/boltops-badge.png)](https://www.boltops.com)
4
+
5
+ NOTE: Am looking for maintainers to help with this gem. Send me an email!
6
+
7
+ IMPORTANT: The next major version of Dynomite will be ActiveModel compatible. A POC is in the [edge](https://github.com/tongueroo/dynomite/tree/edge) branch. It's still very rough and experimental. Would not recommend using it yet, but wanted to note it.
4
8
 
5
9
  A simple wrapper library to make DynamoDB usage a little more friendly. The modeling is ActiveRecord-ish but not exactly because DynamoDB is a different type of database. Examples below explain it best:
6
10
 
11
+ ## Jets Docs
12
+
13
+ * [Database DynamoDB](https://rubyonjets.com/docs/database/dynamodb/)
14
+
7
15
  ## Examples
8
16
 
9
17
  First define a class:
@@ -11,7 +19,7 @@ First define a class:
11
19
  ```ruby
12
20
  class Post < Dynomite::Item
13
21
  # partition_key "id" # optional, defaults to id
14
-
22
+
15
23
  column :id, :title, :desc
16
24
  end
17
25
  ```
@@ -21,7 +29,7 @@ end
21
29
  ```ruby
22
30
  post = Post.new
23
31
  post = post.replace(title: "test title")
24
- post.attrs # {"id" => "generated-id", title" => "my title"}
32
+ post.attrs # {"id" => "generated-id", title" => "test title"}
25
33
  ```
26
34
 
27
35
  `post.attrs[:id]` now contain a generated unique partition_key id. Usually the partition_key is 'id'. You can set your own unique id also by specifying id.
@@ -112,7 +120,7 @@ post.replace
112
120
 
113
121
  puts post.id # 1962DE7D852298C5CDC809C0FEF50D8262CEDF09
114
122
  puts post.name # "My First Post"
115
- ```
123
+ ```
116
124
 
117
125
  Note that any column not defined using the `column` method can still be accessed using the `attrs`
118
126
  method.
@@ -125,15 +133,15 @@ Just add `include ActiveModel::Validations` at the top of your item class.
125
133
  ```ruby
126
134
  class Post < Dynomite::Item
127
135
  include ActiveModel::Validations
128
-
136
+
129
137
  column :id, :name # needed
130
-
138
+
131
139
  validates :id, presence: true
132
140
  validates :name, presence: true
133
141
  end
134
- ```
142
+ ```
135
143
 
136
- **Be sure to define all validated columns using `column` method**.
144
+ **Be sure to define all validated columns using `column` method**.
137
145
 
138
146
  Validations are executed by default as soon as you call the `replace` method, returning `false` on
139
147
  failure. It also can be ran manually using the `valid?` method just like with ActiveRecord models.
@@ -55,6 +55,9 @@ class CreateCommentsMigration < Dynomite::Migration
55
55
  # read_capacity_units: 5,
56
56
  # write_capacity_units: 5
57
57
  # )
58
+
59
+ # set the billing mode to on-demand (NOTE: this overrides provisioned_throughput)
60
+ # t.billing_mode(:pay_per_request)
58
61
  end
59
62
  end
60
63
  end
@@ -62,6 +65,8 @@ end
62
65
  class UpdateCommentsMigration < Dynomite::Migration
63
66
  def up
64
67
  update_table :comments do |t|
68
+ # You can update from provisioned_throughput to on-demand pricing
69
+ # t.billing_mode(:pay_per_request)
65
70
 
66
71
  # t.global_secondary_index do
67
72
  # t.gsi(METHOD, INDEX_NAME) do
@@ -120,4 +125,3 @@ class UpdateCommentsMigration < Dynomite::Migration
120
125
  end
121
126
  end
122
127
  end
123
-
@@ -42,10 +42,24 @@ module Dynomite::DbConfig
42
42
  # This wastes less of the users time.
43
43
  def check_dynamodb_local!(endpoint)
44
44
  return unless endpoint && endpoint.include?("8000")
45
-
46
- open = port_open?("127.0.0.1", 8000, 0.2)
45
+
46
+ host, port = endpoint.gsub("http://", "").split(":")
47
+ ip = get_endpoint_ip(host)
48
+ unless ip
49
+ raise "You have configured your app to use DynamoDB local, but it is not running on the host: #{host}."
50
+ end
51
+
52
+ open = port_open?(ip, 8000, 0.2)
47
53
  unless open
48
- 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"
54
+ raise "You have configured your app to use DynamoDB local, but it is not running. Please start DynamoDB local. Example: brew install --cask dynamodb-local && dynamodb-local"
55
+ end
56
+ end
57
+
58
+ def get_endpoint_ip(host)
59
+ begin
60
+ IPSocket.getaddress(host)
61
+ rescue SocketError
62
+ false # Can return anything you want here
49
63
  end
50
64
  end
51
65
 
data/lib/dynomite/erb.rb CHANGED
@@ -26,7 +26,7 @@ class Dynomite::Erb
26
26
  error_info ||= e.backtrace.grep(/\(erb\)/)[0]
27
27
  raise unless error_info # unable to find the (erb):xxx: error line
28
28
  line = error_info.split(':')[1].to_i
29
- log "Error evaluating ERB template on line #{line.to_s.colorize(:red)} of: #{path.sub(/^\.\//, '').colorize(:green)}"
29
+ log "Error evaluating ERB template on line #{line.to_s.color(:red)} of: #{path.sub(/^\.\//, '').color(:green)}"
30
30
 
31
31
  template_lines = template.split("\n")
32
32
  context = 5 # lines of context
@@ -35,7 +35,7 @@ class Dynomite::Erb
35
35
  template_lines[top..bottom].each_with_index do |line_content, index|
36
36
  line_number = top+index+1
37
37
  if line_number == line
38
- printf("%#{spacing}d %s\n".colorize(:red), line_number, line_content)
38
+ printf("%#{spacing}d %s\n".color(:red), line_number, line_content)
39
39
  else
40
40
  printf("%#{spacing}d %s\n", line_number, line_content)
41
41
  end
data/lib/dynomite/item.rb CHANGED
@@ -4,6 +4,7 @@ require "digest"
4
4
  require "yaml"
5
5
 
6
6
  require "dynomite/reserved_words"
7
+ require "dynomite/query"
7
8
 
8
9
  # The modeling is ActiveRecord-ish but not exactly because DynamoDB is a
9
10
  # different type of database.
@@ -170,9 +171,17 @@ module Dynomite
170
171
  resp.items.map {|i| self.new(i) }
171
172
  end
172
173
 
174
+ # Creates a new chainable ActiveRecord Query-style instance with a certain index_name.
175
+ #
176
+ # Post.index_name("category-index").where(category: "Drama")
177
+ #
178
+ def self.index_name(name)
179
+ _new_query.index_name(name)
180
+ end
181
+
173
182
  # Translates simple query searches:
174
183
  #
175
- # Post.where({category: "Drama"}, index_name: "category-index")
184
+ # Post.index_name("category-index").where(category: "Drama")
176
185
  #
177
186
  # translates to
178
187
  #
@@ -183,34 +192,8 @@ module Dynomite
183
192
  # expression_attribute_values: { ":category_value" => category },
184
193
  # key_condition_expression: "#category_name = :category_value",
185
194
  # )
186
- #
187
- # TODO: Implement nicer where syntax with index_name as a chained method.
188
- #
189
- # Post.where({category: "Drama"}, {index_name: "category-index"})
190
- # VS
191
- # Post.where(category: "Drama").index_name("category-index")
192
- def self.where(attributes, options={})
193
- raise "attributes.size == 1 only supported for now" if attributes.size != 1
194
-
195
- attr_name = attributes.keys.first
196
- attr_value = attributes[attr_name]
197
-
198
- # params = {
199
- # expression_attribute_names: { "#category_name" => "category" },
200
- # expression_attribute_values: { ":category_value" => "Entertainment" },
201
- # key_condition_expression: "#category_name = :category_value",
202
- # }
203
- name_key, value_key = "##{attr_name}_name", ":#{attr_name}_value"
204
- params = {
205
- expression_attribute_names: { name_key => attr_name },
206
- expression_attribute_values: { value_key => attr_value },
207
- key_condition_expression: "#{name_key} = #{value_key}",
208
- }
209
- # Allow direct access to override params passed to dynamodb query options.
210
- # This is is how index_name is passed:
211
- params = params.merge(options)
212
-
213
- query(params)
195
+ def self.where(attributes)
196
+ _new_query.where(attributes)
214
197
  end
215
198
 
216
199
  def self.replace(attrs)
@@ -343,5 +326,9 @@ module Dynomite
343
326
  @attrs[name.to_s] = value
344
327
  end
345
328
  end
329
+
330
+ def self._new_query
331
+ Dynomite::Query.new(self, {})
332
+ end
346
333
  end
347
334
  end
@@ -1,5 +1,12 @@
1
1
  class Dynomite::Migration
2
2
  class Dsl
3
+ ATTRIBUTES = %i[
4
+ key_schema
5
+ attribute_definitions
6
+ table_name
7
+ billing_mode
8
+ ].freeze
9
+
3
10
  autoload :Common, "dynomite/migration/common"
4
11
  autoload :BaseSecondaryIndex, "dynomite/migration/dsl/base_secondary_index"
5
12
  autoload :LocalSecondaryIndex, "dynomite/migration/dsl/local_secondary_index"
@@ -8,15 +15,16 @@ class Dynomite::Migration
8
15
  include Dynomite::DbConfig
9
16
  include Common
10
17
 
11
- attr_accessor :key_schema, :attribute_definitions
12
- attr_accessor :table_name
18
+ attr_accessor(*ATTRIBUTES)
19
+
13
20
  def initialize(method_name, table_name, &block)
14
21
  @method_name = method_name
15
22
  @table_name = table_name
16
23
  @block = block
17
24
 
18
- # Dsl fills in atttributes in as methods are called within the block.
25
+ # Dsl fills in attributes in as methods are called within the block.
19
26
  # Attributes for both create_table and updated_table:
27
+ @billing_mode = 'PROVISIONED'
20
28
  @attribute_definitions = []
21
29
  @provisioned_throughput = {
22
30
  read_capacity_units: 5,
@@ -31,6 +39,14 @@ class Dynomite::Migration
31
39
  @lsi_indexes = []
32
40
  end
33
41
 
42
+ # t.billing_mode(:pay_per_request)
43
+ # t.billing_mode(:provisioned) # default value
44
+ def billing_mode(mode = nil)
45
+ return @billing_mode if mode.nil?
46
+
47
+ @billing_mode = mode.to_s.upcase
48
+ end
49
+
34
50
  # t.gsi(:create) do |i|
35
51
  # i.partition_key "category:string"
36
52
  # i.sort_key "created_at:string" # optional
@@ -81,9 +97,10 @@ class Dynomite::Migration
81
97
  table_name: namespaced_table_name,
82
98
  key_schema: @key_schema,
83
99
  attribute_definitions: @attribute_definitions,
84
- provisioned_throughput: @provisioned_throughput
100
+ billing_mode: @billing_mode
85
101
  }
86
102
 
103
+ params[:provisioned_throughput] = @provisioned_throughput if @billing_mode == 'PROVISIONED'
87
104
  params[:local_secondary_indexes] = lsi_secondary_index_creates unless @lsi_indexes.empty?
88
105
  params[:global_secondary_indexes] = gsi_secondary_index_creates unless @gsi_indexes.empty?
89
106
  params
@@ -95,12 +112,15 @@ class Dynomite::Migration
95
112
  params = {
96
113
  table_name: namespaced_table_name,
97
114
  attribute_definitions: @attribute_definitions,
115
+ billing_mode: @billing_mode
98
116
  # update table take values only some values for the "parent" table
99
117
  # no key_schema, update_table does not handle key_schema for the "parent" table
100
118
  }
101
119
  # only set "parent" table provisioned_throughput if user actually invoked
102
120
  # it in the dsl
103
- params[:provisioned_throughput] = @provisioned_throughput if @provisioned_throughput_set_called
121
+ if @provisioned_throughput_set_called && @billing_mode == 'PROVISIONED'
122
+ params[:provisioned_throughput] = @provisioned_throughput
123
+ end
104
124
  params[:global_secondary_index_updates] = global_secondary_index_updates
105
125
  params
106
126
  end
@@ -31,7 +31,7 @@ class Dynomite::Migration
31
31
 
32
32
  puts "DynamoDB Table: #{@table_name} Status: #{result.table_description.table_status}"
33
33
  rescue Aws::DynamoDB::Errors::ServiceError => error
34
- puts "Unable to #{@method_name.to_s.gsub('_',' ')}: #{error.message}".colorize(:red)
34
+ puts "Unable to #{@method_name.to_s.gsub('_',' ')}: #{error.message}".color(:red)
35
35
  end
36
36
  end
37
37
  end
@@ -31,7 +31,7 @@ class Dynomite::Migration
31
31
  table_name: table_name,
32
32
  partition_key: @options[:partition_key],
33
33
  sort_key: @options[:sort_key],
34
- provisioned_throughput: @options[:provisioned_throughput] || 5,
34
+ provisioned_throughput: @options[:provisioned_throughput] || 5
35
35
  )
36
36
  end
37
37
 
@@ -5,6 +5,7 @@ class <%= @migration_class_name %> < Dynomite::Migration
5
5
  <% if @sort_key # so extra spaces are not added when generated -%>
6
6
  t.sort_key "<%= @sort_key %>" # optional
7
7
  <% end -%>
8
+ t.billing_mode(:PROVISIONED)
8
9
  t.provisioned_throughput(<%= @provisioned_throughput %>) # sets both read and write, defaults to 5 when not set
9
10
 
10
11
  # Instead of using partition_key and sort_key you can set the
@@ -0,0 +1,48 @@
1
+ module Dynomite
2
+ class Query
3
+ include Enumerable
4
+
5
+ def initialize(item, params)
6
+ @item = item
7
+ @params = params
8
+ end
9
+
10
+ def <<(item)
11
+ raise NotImplementedError
12
+ end
13
+
14
+ def inspect
15
+ "#<Dynomite::Query [#{first(2).map(&:inspect).join(', ')}, ...]>"
16
+ end
17
+
18
+ def each(&block)
19
+ run_query.each(&block)
20
+ end
21
+
22
+ def index_name(name)
23
+ self.class.new(@item, @params.merge(index_name: name))
24
+ end
25
+
26
+ def where(attributes)
27
+ raise "attributes.size == 1 only supported for now" if attributes.size != 1
28
+
29
+ attr_name = attributes.keys.first
30
+ attr_value = attributes[attr_name]
31
+
32
+ name_key, value_key = "##{attr_name}_name", ":#{attr_name}_value"
33
+ params = {
34
+ expression_attribute_names: { name_key => attr_name },
35
+ expression_attribute_values: { value_key => attr_value },
36
+ key_condition_expression: "#{name_key} = #{value_key}",
37
+ }
38
+
39
+ self.class.new(@item, @params.merge(params))
40
+ end
41
+
42
+ private
43
+
44
+ def run_query
45
+ @query ||= @item.query(@params)
46
+ end
47
+ end
48
+ end
@@ -1,3 +1,3 @@
1
1
  module Dynomite
2
- VERSION = "1.2.4"
2
+ VERSION = "1.2.7"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynomite
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.4
4
+ version: 1.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tung Nguyen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-13 00:00:00.000000000 Z
11
+ date: 2022-06-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -129,6 +129,7 @@ files:
129
129
  - lib/dynomite/migration/generator.rb
130
130
  - lib/dynomite/migration/templates/create_table.rb
131
131
  - lib/dynomite/migration/templates/update_table.rb
132
+ - lib/dynomite/query.rb
132
133
  - lib/dynomite/reserved_words.rb
133
134
  - lib/dynomite/version.rb
134
135
  homepage: https://github.com/tongueroo/dynomite
@@ -149,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
150
  - !ruby/object:Gem::Version
150
151
  version: '0'
151
152
  requirements: []
152
- rubygems_version: 3.0.6
153
+ rubygems_version: 3.3.12
153
154
  signing_key:
154
155
  specification_version: 4
155
156
  summary: ActiveRecord-ish Dynamodb Model