Dynamoid 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source "http://rubygems.org"
2
+ gem 'activemodel'
3
+ gem 'tzinfo'
4
+ gem 'aws-sdk'
5
+
6
+ # Add dependencies required to use your gem here.
7
+ # Example:
8
+ # gem "activesupport", ">= 2.3.5"
9
+
10
+ # Add dependencies to develop your gem here.
11
+ # Include everything needed to run rake, tests, features, etc.
12
+ group :development do
13
+ gem "mocha"
14
+ gem "rake"
15
+ gem "rspec"
16
+ gem "bundler"
17
+ gem "jeweler"
18
+ gem "rcov"
19
+ end
@@ -0,0 +1,58 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activemodel (3.1.3)
5
+ activesupport (= 3.1.3)
6
+ builder (~> 3.0.0)
7
+ i18n (~> 0.6)
8
+ activesupport (3.1.3)
9
+ multi_json (~> 1.0)
10
+ aws-sdk (1.3.5)
11
+ httparty (~> 0.7)
12
+ json (~> 1.4)
13
+ nokogiri (>= 1.4.4)
14
+ uuidtools (~> 2.1)
15
+ builder (3.0.0)
16
+ diff-lcs (1.1.3)
17
+ git (1.2.5)
18
+ httparty (0.8.1)
19
+ multi_json
20
+ multi_xml
21
+ i18n (0.6.0)
22
+ jeweler (1.6.4)
23
+ bundler (~> 1.0)
24
+ git (>= 1.2.5)
25
+ rake
26
+ json (1.6.5)
27
+ metaclass (0.0.1)
28
+ mocha (0.10.0)
29
+ metaclass (~> 0.0.1)
30
+ multi_json (1.0.4)
31
+ multi_xml (0.4.1)
32
+ nokogiri (1.5.0)
33
+ rake (0.9.2.2)
34
+ rcov (0.9.11)
35
+ rspec (2.8.0)
36
+ rspec-core (~> 2.8.0)
37
+ rspec-expectations (~> 2.8.0)
38
+ rspec-mocks (~> 2.8.0)
39
+ rspec-core (2.8.0)
40
+ rspec-expectations (2.8.0)
41
+ diff-lcs (~> 1.1.2)
42
+ rspec-mocks (2.8.0)
43
+ tzinfo (0.3.31)
44
+ uuidtools (2.1.2)
45
+
46
+ PLATFORMS
47
+ ruby
48
+
49
+ DEPENDENCIES
50
+ activemodel
51
+ aws-sdk
52
+ bundler
53
+ jeweler
54
+ mocha
55
+ rake
56
+ rcov
57
+ rspec
58
+ tzinfo
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Synthetic LLC
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,66 @@
1
+ # Dynamoid
2
+
3
+ Dynamoid is an ORM for Amazon's DynamoDB for Ruby applications. It provides similar functionality to ActiveRecord and improves on Amazon's existing [HashModel](http://docs.amazonwebservices.com/AWSRubySDK/latest/AWS/Record/HashModel.html) by providing better searching tools, native association support, and a local adapter for offline development.
4
+
5
+ ## Warning!
6
+
7
+ I'm still working on this gem a lot. Associations aren't working and that's a bummer, and you can only use the old-school ActiveRecord style finders like ```find_all_by_<attribute_name>``` or directly finding by an ID.
8
+
9
+ ## Usage
10
+
11
+ Using Dynamoid is pretty simple. First you need to initialize it to get it going, so put code similar to this somewhere (a Rails initializer would be a great place for this if you're using Rails):
12
+
13
+ ```ruby
14
+ Dynamoid.configure do |config|
15
+ config.adapter = 'local' # This adapter allows offline development without connecting to the DynamoDB servers.
16
+ # config.adapter = 'aws_sdk' # This adapter establishes a connection to the DynamoDB servers using's Amazon's own awful AWS gem.
17
+ # config.access_key = 'access_key' # If connecting to DynamoDB, your access key is required.
18
+ # config.secret_key = 'secret_key' # So is your secret key.
19
+ config.namespace = "dynamoid_#{Rails.application.class.parent_name}_#{Rails.env}" # To namespace tables created by Dynamoid from other tables you might have.
20
+ config.warn_on_scan = true # Output a warning to stdout when you perform a scan rather than a query on a table
21
+ end
22
+
23
+ ```
24
+
25
+ Inside your model:
26
+
27
+ ```ruby
28
+ class User
29
+ include Dynamoid::Document
30
+
31
+ field :name
32
+ field :email
33
+
34
+ index :name
35
+ index :email
36
+ index [:name, :email]
37
+
38
+ end
39
+ ```
40
+
41
+ Right now, you can only do a couple things with this amazing functionality:
42
+
43
+ ```ruby
44
+ u = User.new(:name => 'Josh')
45
+ u.email = 'josh@joshsymonds.com'
46
+ u.save
47
+
48
+ u == User.find(u.id)
49
+ u == User.find_by_name('Josh')
50
+ u == User.find_by_name_and_email('Josh','josh@joshsymonds.com')
51
+ ```
52
+
53
+ Not super exciting yet, true... but it's getting there!
54
+
55
+ ## Credits
56
+
57
+ Dynamoid borrows code, structure, and even its name very liberally from the truly amazing [Mongoid](https://github.com/mongoid/mongoid). Without Mongoid to crib from none of this would have been possible, and I hope they don't mind me reusing their very awesome ideas to make DynamoDB just as accessible to the Ruby world as MongoDB.
58
+
59
+ ## Running the tests
60
+
61
+ The tests can be run in the simple predictable way with ```rake```. However, if you provide environment variables for ACCESS_KEY and SECRET_KEY, the tests will use the aws_sdk adapter rather than the local adapter: ```ACCESS_KEY=<accesskey> SECRET_KEY=<secretkey> rake```. Keep in mind this takes much, much longer than the local tests.
62
+
63
+ ## Copyright
64
+
65
+ Copyright (c) 2012 Josh Symonds. See LICENSE.txt for further details.
66
+
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "Dynamoid"
18
+ gem.homepage = "http://github.com/Veraticus/Dynamoid"
19
+ gem.license = "MIT"
20
+ gem.summary = "Dynamoid is an ORM for Amazon's DynamoDB"
21
+ gem.description = "Dynamoid is an ORM for Amazon's DynamoDB that supports offline development, associations, querying, and everything else you'd expect from an ActiveRecord-style replacement."
22
+ gem.email = "josh@joshsymonds.com"
23
+ gem.authors = ["Josh Symonds"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :default => :spec
40
+
41
+ require 'rake/rdoctask'
42
+ Rake::RDocTask.new do |rdoc|
43
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "dynamoid #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,31 @@
1
+ require "delegate"
2
+ require "time"
3
+ require "securerandom"
4
+ require "active_support/core_ext"
5
+ require 'active_support/json'
6
+ require "active_support/inflector"
7
+ require "active_support/lazy_load_hooks"
8
+ require "active_support/time_with_zone"
9
+ require "active_model"
10
+
11
+ require 'dynamoid/errors'
12
+ require 'dynamoid/attributes'
13
+ require 'dynamoid/fields'
14
+ require 'dynamoid/indexes'
15
+ require 'dynamoid/persistence'
16
+ require 'dynamoid/finders'
17
+ require 'dynamoid/config'
18
+ require 'dynamoid/components'
19
+ require 'dynamoid/document'
20
+ require 'dynamoid/adapter'
21
+
22
+ module Dynamoid
23
+ extend self
24
+
25
+ def configure
26
+ block_given? ? yield(Config) : Config
27
+ Dynamoid::Adapter.reconnect!
28
+ end
29
+ alias :config :configure
30
+
31
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+ module Dynamoid #:nodoc:
3
+
4
+ module Adapter
5
+ extend self
6
+
7
+ def adapter
8
+ reconnect! unless @adapter
9
+ @adapter
10
+ end
11
+
12
+ def reconnect!
13
+ require "dynamoid/adapter/#{Dynamoid::Config.adapter}" unless Dynamoid::Adapter.const_defined?(Dynamoid::Config.adapter.camelcase)
14
+ @adapter = Dynamoid::Adapter.const_get(Dynamoid::Config.adapter.camelcase)
15
+ @adapter.connect! if @adapter.respond_to?(:connect!)
16
+ end
17
+
18
+ def method_missing(method, *args)
19
+ return @adapter.send(method, *args) if @adapter.respond_to?(method)
20
+ super
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,99 @@
1
+ require 'aws'
2
+
3
+ module Dynamoid
4
+ module Adapter
5
+ module AwsSdk
6
+ extend self
7
+ @@connection = nil
8
+
9
+ def connect!
10
+ @@connection = AWS::DynamoDB.new(:access_key_id => Dynamoid::Config.access_key, :secret_access_key => Dynamoid::Config.secret_key)
11
+ end
12
+
13
+ def connection
14
+ @@connection
15
+ end
16
+
17
+ # BatchGetItem
18
+ def batch_get_item(options)
19
+ batch = AWS::DynamoDB::BatchGet.new(:config => @@connection.config)
20
+ options.each do |t, ids|
21
+ batch.table(t, :all, Array(ids))
22
+ end
23
+ hash = Hash.new{|h, k| h[k] = []}
24
+ batch.each do |table_name, attributes|
25
+ hash[table_name] << attributes.symbolize_keys!
26
+ end
27
+ hash
28
+ end
29
+
30
+ # CreateTable
31
+ def create_table(table_name, key)
32
+ table = @@connection.tables.create(table_name, 10, 5, :hash_key => {key.to_sym => :string})
33
+ sleep 0.5 while table.status == :creating
34
+ return table
35
+ end
36
+
37
+ # DeleteItem
38
+ def delete_item(table_name, key)
39
+ table = @@connection.tables[table_name]
40
+ table.load_schema
41
+ result = table.items[key]
42
+ result.delete unless result.attributes.to_h.empty?
43
+ true
44
+ end
45
+
46
+ # DeleteTable
47
+ def delete_table(table_name)
48
+ @@connection.tables[table_name].delete
49
+ end
50
+
51
+ # DescribeTable
52
+
53
+ # GetItem
54
+ def get_item(table_name, key)
55
+ table = @@connection.tables[table_name]
56
+ table.load_schema
57
+ result = table.items[key].attributes.to_h
58
+ if result.empty?
59
+ nil
60
+ else
61
+ result.symbolize_keys!
62
+ end
63
+ end
64
+
65
+ # ListTables
66
+ def list_tables
67
+ @@connection.tables.collect(&:name)
68
+ end
69
+
70
+ # PutItem
71
+ def put_item(table_name, object)
72
+ table = @@connection.tables[table_name]
73
+ table.load_schema
74
+ table.items.create(object.delete_if{|k, v| v.nil?})
75
+ end
76
+
77
+ # Query
78
+ def query(table_name, id)
79
+ get_item(table_name, id)
80
+ end
81
+
82
+ # Scan
83
+ def scan(table_name, scan_hash)
84
+ table = @@connection.tables[table_name]
85
+ table.load_schema
86
+ results = []
87
+ table.items.select do |data|
88
+ attributes = data.attributes.symbolize_keys!
89
+ results << attributes if scan_hash.all?{|k, v| !attributes[k].nil? && attributes[k] == v}
90
+ end
91
+ results
92
+ end
93
+
94
+ # UpdateItem
95
+
96
+ # UpdateTable
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,77 @@
1
+ module Dynamoid
2
+ module Adapter
3
+ module Local
4
+ extend self
5
+ # Gimpy hash that should be somewhat equivalent to what Amazon's actual DynamoDB, for offline development.
6
+
7
+ def data
8
+ @data ||= {}
9
+ end
10
+
11
+ def reset_data
12
+ self.data.each {|k, v| v[:data] = {}}
13
+ end
14
+
15
+ # BatchGetItem
16
+ def batch_get_item(options)
17
+ Hash.new { |h, k| h[k] = Array.new }.tap do |hash|
18
+ options.each do |table_name, keys|
19
+ table = data[table_name]
20
+ Array(keys).each do |key|
21
+ hash[table_name] << table[:data][key]
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ # CreateTable
28
+ def create_table(table_name, key)
29
+ data[table_name] = {:id => key, :data => {}}
30
+ end
31
+
32
+ # DeleteItem
33
+ def delete_item(table_name, key)
34
+ data[table_name][:data].delete(key)
35
+ end
36
+
37
+ # DeleteTable
38
+ def delete_table(table_name)
39
+ data.delete(table_name)
40
+ end
41
+
42
+ # DescribeTable
43
+
44
+ # GetItem
45
+ def get_item(table_name, key)
46
+ data[table_name][:data][key]
47
+ end
48
+
49
+ # ListTables
50
+ def list_tables
51
+ data.keys
52
+ end
53
+
54
+ # PutItem
55
+ def put_item(table_name, object)
56
+ table = data[table_name]
57
+ table[:data][object[table[:id]]] = object
58
+ end
59
+
60
+ # Query
61
+ def query(table_name, id)
62
+ get_item(table_name, id)
63
+ end
64
+
65
+ # Scan
66
+ def scan(table_name, scan_hash)
67
+ return [] if data[table_name].nil?
68
+ data[table_name][:data].values.select{|d| scan_hash.all?{|k, v| !d[k].nil? && d[k] == v}}
69
+ end
70
+
71
+ # UpdateItem
72
+
73
+ # UpdateTable
74
+
75
+ end
76
+ end
77
+ end