dynomite 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 65cbe54a9b7faa1698f39915100793cb5c004725f16d2bd3cb5b73c55b87a92f
4
+ data.tar.gz: 0ceb719e4386c73ef6fd4ea108d7114d09e1da1bfab35d8695203422b998ea56
5
+ SHA512:
6
+ metadata.gz: 2c36491debda7b2697e0f44442925420d29de096e5d28c73ae234cbb7f1dd939d491b965305fd7627f1700c6182752db8c7dff032e2502dfe8425a34b4291af5
7
+ data.tar.gz: 88991c61e3cd38d5b08eeb3c4e951ddb7b5a7bb3b26bae0bfac90140d3f370a7147a39af2c1b80c6f086921bd5e0c424a5e2e4f3a6d4551c48f52acfd207d3c2
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --require spec_helper
2
+ --color
3
+ --format documentation
@@ -0,0 +1,28 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ This project *tries* to adhere to [Semantic Versioning](http://semver.org/), even before v1.0.
5
+
6
+ ## [1.0.5]
7
+ - fix jets dynamodb:migrate tip
8
+
9
+ ## [1.0.4]
10
+ - Add and use log method instead of puts to write to stderr by default
11
+
12
+ ## [1.0.3]
13
+ - rename APP_ROOT to JETS_ROOT
14
+
15
+ ## [1.0.2]
16
+ - to_json for json rendering
17
+
18
+ ## [1.0.1]
19
+ - Check dynamodb local is running when configured
20
+
21
+ ## [1.0.0]
22
+ - LSI support
23
+ - automatically infer table_name
24
+ - automatically infer create_table and update_table migrations types
25
+
26
+ ## [0.3.0]
27
+ - DSL methods now available: create_table, update_table
28
+ - Also can add GSI indexes within update_table with: i.gsi
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in dynomite.gemspec
6
+ gemspec
@@ -0,0 +1,141 @@
1
+ # Dynomite
2
+
3
+ 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:
4
+
5
+ ## Examples
6
+
7
+ First define a class:
8
+
9
+ ```ruby
10
+ class Post < Dynomite::Item
11
+ # partition_key "id" # optional, defaults to id
12
+ end
13
+ ```
14
+
15
+ ### Create
16
+
17
+ ```ruby
18
+ post = Post.new
19
+ post = post.replace(title: "test title")
20
+ post.attrs # {"id" => "generated-id", title" => "my title"}
21
+ ```
22
+
23
+ `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.
24
+
25
+ ```ruby
26
+ post = Post.new(id: "myid", title: "my title")
27
+ post.replace
28
+ post.attrs # {"id" => "myid", title" => "my title"}
29
+ ```
30
+
31
+ Note that the replace method replaces the entire item, so you need to merge the attributes if you want to keep the other attributes. Know this is weird, but this is how DynamoDB works.
32
+
33
+ ### Find
34
+
35
+ ```ruby
36
+ post = Post.find("myid")
37
+ post.attrs = post.attrs.deep_merge("desc": "my desc") # keeps title field
38
+ post.replace
39
+ post.attrs # {"id" => "myid", title" => "my title", desc: "my desc"}
40
+ ```
41
+
42
+ The convenience `attrs` method performs a deep_merge:
43
+
44
+ ```ruby
45
+ post = Post.find("myid")
46
+ post.attrs("desc": "my desc 2") # <= does a deep_merge
47
+ post.replace
48
+ post.attrs # {"id" => "myid", title" => "my title", desc: "my desc 2"}
49
+ ```
50
+
51
+ Note, a race condition edge case can exist when several concurrent replace
52
+ calls are happening. This is why the interface is called replace to
53
+ emphasis that possibility.
54
+
55
+ ### Delete
56
+
57
+ ```ruby
58
+ resp = Post.delete("myid") # dynamodb client resp
59
+ # or
60
+ post = Post.find("myid")
61
+ resp = post.delete # dynamodb client resp
62
+ ```
63
+
64
+ ### Scan
65
+
66
+ ```ruby
67
+ options = {}
68
+ posts = Post.scan(options)
69
+ posts # Array of Post items. [Post.new, Post.new, ...]
70
+ ```
71
+
72
+ ### Query
73
+
74
+ ```ruby
75
+ posts = Post.query(
76
+ index_name: 'category-index',
77
+ expression_attribute_names: { "#category_name" => "category" },
78
+ expression_attribute_values: { ":category_value" => "Entertainment" },
79
+ key_condition_expression: "#category_name = :category_value",
80
+ )
81
+ posts # Array of Post items. [Post.new, Post.new, ...]
82
+ ```
83
+
84
+ ### Where
85
+
86
+ The where could be prettied up. Appreciate any pull requests.
87
+
88
+ ```ruby
89
+ Post.where({category: "Drama"}, {index_name: "category-index"})
90
+ ```
91
+
92
+ Examples are also in [item_spec.rb](spec/lib/dynomite/item_spec.rb).
93
+
94
+ ## Migration Support
95
+
96
+ Dynomite supports ActiveRecord-like migrations. Here's a short example:
97
+
98
+ ```ruby
99
+ class CreateCommentsMigration < Dynomite::Migration
100
+ def up
101
+ create_table :comments do |t|
102
+ t.partition_key "post_id:string" # required
103
+ t.sort_key "created_at:string" # optional
104
+ t.provisioned_throughput(5) # sets both read and write, defaults to 5 when not set
105
+ end
106
+ end
107
+ end
108
+ ```
109
+
110
+ More examples are in the [docs/migrations](docs/migrations) folder.
111
+
112
+ ## Installation
113
+
114
+ Add this line to your application's Gemfile:
115
+
116
+ ```ruby
117
+ gem 'dynomite'
118
+ ```
119
+
120
+ And then execute:
121
+
122
+ $ bundle
123
+
124
+ Or install it yourself as:
125
+
126
+ $ gem install dynomite
127
+
128
+ ## Development
129
+
130
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
131
+
132
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
133
+
134
+ ## Contributing
135
+
136
+ Bug reports and pull requests are welcome on GitHub at https://github.com/tongueroo/dynomite.
137
+
138
+ ### TODO
139
+
140
+ * improve Post.where. Something like `Post.index_name("user_id").where(category_name: "Entertainment")` would be nice.
141
+ * implement `post.update` with `db.update_item` in a Ruby-ish way
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "dynomite"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,123 @@
1
+ # Note: table name created will be namespaced based on
2
+ # Dynomite::Migration.table_namespace. This can be set in
3
+ # config/dynamodb.yml
4
+ #
5
+ # development:
6
+ # table_namespace: "mynamespace"
7
+ #
8
+ # This results in:
9
+ # create_table "posts"
10
+ # Produces:
11
+ # table name: "mynamespace-posts"
12
+ #
13
+ # When you're in a in Jets project you can set the namespace based on
14
+ # Jets.config.table_namespace, which is based on the project name and
15
+ # a short version of the environment. Example:
16
+ #
17
+ # `config/dynamodb.yml`:
18
+ # development:
19
+ # table_namespace: <%= Jets.config.table_namespace %>
20
+ #
21
+ # If your project_name is demo and environment is production:
22
+ # create_table "posts" => table name: "demo-prod-posts"
23
+ #
24
+ # If your project_name is proj and environment is staging:
25
+ # create_table "posts" => table name: "demo-stag-posts"
26
+ #
27
+ # If your project_name is proj and environment is development:
28
+ # create_table "posts" => table name: "demo-dev-posts"
29
+ #
30
+ # If the table_namespace is set to a blank string or nil, then a namespace
31
+ # will not be prepended at all.
32
+
33
+ class CreateCommentsMigration < Dynomite::Migration
34
+ def up
35
+ create_table :comments do |t|
36
+ t.partition_key "post_id:string" # required
37
+ t.sort_key "created_at:string" # optional
38
+ t.provisioned_throughput(5) # sets both read and write, defaults to 5 when not set
39
+
40
+ # Instead of using partition_key and sort_key you can set the
41
+ # key schema directly also
42
+ # t.key_schema([
43
+ # {attribute_name: "id", :key_type=>"HASH"},
44
+ # {attribute_name: "created_at", :key_type=>"RANGE"}
45
+ # ])
46
+ # t.attribute_definitions([
47
+ # {attribute_name: "id", attribute_type: "N"},
48
+ # {attribute_name: "created_at", attribute_type: "S"}
49
+ # ])
50
+
51
+ # other ways to set provisioned_throughput
52
+ # t.provisioned_throughput(:read, 10)
53
+ # t.provisioned_throughput(:write, 10)
54
+ # t.provisioned_throughput(
55
+ # read_capacity_units: 5,
56
+ # write_capacity_units: 5
57
+ # )
58
+ end
59
+ end
60
+ end
61
+
62
+ class UpdateCommentsMigration < Dynomite::Migration
63
+ def up
64
+ update_table :comments do |t|
65
+
66
+ # t.global_secondary_index do
67
+ # t.gsi(METHOD, INDEX_NAME) do
68
+
69
+ # You normally create an index like so:
70
+ #
71
+ # t.gsi(:create) do |i|
72
+ # i.partition_key = "post_id:string" # partition_key is required
73
+ # i.sort_key = "updated_at:string" # sort_key is optional
74
+ # end
75
+ #
76
+ # The index name will be inferred from the partition_key and sort_key when
77
+ # not explicitly set. Examples:
78
+ #
79
+ # index_name = "#{partition_key}-#{sort_key}-index"
80
+ # index_name = "post_id-index" # no sort key
81
+ # index_name = "post_id-updated_at-index" # has sort key
82
+ #
83
+ # The inference allows you to not have to worry about the index
84
+ # naming scheme. You can still set the index_name explicitly like so:
85
+ #
86
+ # t.gsi(:create, "post_id-updated_at-index") do |i|
87
+ # i.partition_key = "post_id:string" # partition_key is required
88
+ # i.sort_key = "updated_at:string" # sort_key is optional
89
+ # end
90
+ #
91
+ t.gsi(:create) do |i|
92
+ i.partition_key "post_id:string"
93
+ i.sort_key "updated_at:string" # optional
94
+
95
+ # translates to
96
+ # i.key_schema({...})
97
+ # also makes sure that the schema_keys are added to the attributes_definitions
98
+
99
+ # t.projected_attributes(:all) # default if not called
100
+ # t.projected_attributes(:keys_only) # other ways to call
101
+ # t.projected_attributes([:id, :body, :tags, :updated_at])
102
+ # translates to:
103
+ # Valid Values: ALL | KEYS_ONLY | INCLUDE
104
+ # t.projection(
105
+ # projection_type: :all, # defaults to all
106
+ # )
107
+ # t.projection(
108
+ # projection_type: :include, # defaults to all
109
+ # non_key_attributes: [:id, :body, :tags, :updated_at], # defaults to all
110
+ # )
111
+
112
+ i.provisioned_throughput(10)
113
+ end
114
+
115
+ t.gsi(:update, "category-index") do |i|
116
+ i.provisioned_throughput(10)
117
+ end
118
+
119
+ t.gsi(:delete, "category-index")
120
+ end
121
+ end
122
+ end
123
+
@@ -0,0 +1,36 @@
1
+ class CreateCommentsMigration < Dynomite::Migration
2
+ def up
3
+ create_table :comments do |t|
4
+ t.partition_key "post_id:string" # required
5
+ t.sort_key "created_at:string" # optional
6
+ t.provisioned_throughput(5) # sets both read and write, defaults to 5 when not set
7
+
8
+ t.lsi do |i|
9
+ i.partition_key "user_id:string"
10
+ i.sort_key "updated_at:string" # optional
11
+
12
+ i.provisioned_throughput(10)
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ class UpdateCommentsMigration < Dynomite::Migration
19
+ def up
20
+ update_table :comments do |t|
21
+ t.gsi(:create) do |i|
22
+ i.partition_key "post_id:string"
23
+ i.sort_key "updated_at:string" # optional
24
+
25
+ i.provisioned_throughput(10)
26
+ end
27
+
28
+ t.gsi(:update, "update-me-index") do |i|
29
+ i.provisioned_throughput(10)
30
+ end
31
+
32
+ t.gsi(:delete, "delete-me-index")
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "dynomite/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "dynomite"
8
+ spec.version = Dynomite::VERSION
9
+ spec.authors = ["Tung Nguyen"]
10
+ spec.email = ["tongueroo@gmail.com"]
11
+
12
+ spec.summary = %q{ActiveRecord-ish Dynamodb Model}
13
+ spec.description = %q{ActiveRecord-ish Dynamodb Model}
14
+ spec.homepage = "https://github.com/tongueroo/dynomite"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_dependency "activesupport"
24
+ spec.add_dependency "aws-sdk-dynamodb"
25
+
26
+ spec.add_development_dependency "bundler"
27
+ spec.add_development_dependency "rake"
28
+ spec.add_development_dependency "rspec"
29
+ end
@@ -0,0 +1,23 @@
1
+ $:.unshift(File.expand_path("../", __FILE__))
2
+ require "dynomite/version"
3
+
4
+ module Dynomite
5
+ ATTRIBUTE_TYPES = {
6
+ 'string' => 'S',
7
+ 'number' => 'N',
8
+ 'binary' => 'B',
9
+ 's' => 'S',
10
+ 'n' => 'N',
11
+ 'b' => 'B',
12
+ }
13
+
14
+ autoload :Migration, "dynomite/migration"
15
+ autoload :Dsl, "dynomite/dsl"
16
+ autoload :DbConfig, "dynomite/db_config"
17
+ autoload :Item, "dynomite/item"
18
+ autoload :Core, "dynomite/core"
19
+ autoload :Erb, "dynomite/erb"
20
+ autoload :Log, "dynomite/log"
21
+
22
+ extend Core
23
+ end