dynamoid 0.0.2

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 (54) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Dynamoid.gemspec +116 -0
  4. data/Gemfile +19 -0
  5. data/Gemfile.lock +58 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.markdown +86 -0
  8. data/Rakefile +49 -0
  9. data/VERSION +1 -0
  10. data/lib/dynamoid.rb +32 -0
  11. data/lib/dynamoid/adapter.rb +24 -0
  12. data/lib/dynamoid/adapter/aws_sdk.rb +100 -0
  13. data/lib/dynamoid/adapter/local.rb +77 -0
  14. data/lib/dynamoid/associations.rb +54 -0
  15. data/lib/dynamoid/associations/association.rb +80 -0
  16. data/lib/dynamoid/associations/belongs_to.rb +39 -0
  17. data/lib/dynamoid/associations/has_and_belongs_to_many.rb +34 -0
  18. data/lib/dynamoid/associations/has_many.rb +33 -0
  19. data/lib/dynamoid/associations/has_one.rb +36 -0
  20. data/lib/dynamoid/attributes.rb +37 -0
  21. data/lib/dynamoid/components.rb +25 -0
  22. data/lib/dynamoid/config.rb +19 -0
  23. data/lib/dynamoid/config/options.rb +74 -0
  24. data/lib/dynamoid/document.rb +35 -0
  25. data/lib/dynamoid/errors.rb +8 -0
  26. data/lib/dynamoid/fields.rb +32 -0
  27. data/lib/dynamoid/finders.rb +62 -0
  28. data/lib/dynamoid/indexes.rb +59 -0
  29. data/lib/dynamoid/persistence.rb +35 -0
  30. data/lib/dynamoid/relations.rb +21 -0
  31. data/spec/app/models/address.rb +5 -0
  32. data/spec/app/models/magazine.rb +6 -0
  33. data/spec/app/models/sponsor.rb +6 -0
  34. data/spec/app/models/subscription.rb +6 -0
  35. data/spec/app/models/user.rb +13 -0
  36. data/spec/dynamoid/adapter/aws_sdk_spec.rb +123 -0
  37. data/spec/dynamoid/adapter/local_spec.rb +150 -0
  38. data/spec/dynamoid/adapter_spec.rb +13 -0
  39. data/spec/dynamoid/associations/association_spec.rb +71 -0
  40. data/spec/dynamoid/associations/belongs_to_spec.rb +50 -0
  41. data/spec/dynamoid/associations/has_and_belongs_to_many_spec.rb +30 -0
  42. data/spec/dynamoid/associations/has_many_spec.rb +28 -0
  43. data/spec/dynamoid/associations/has_one_spec.rb +37 -0
  44. data/spec/dynamoid/associations_spec.rb +16 -0
  45. data/spec/dynamoid/attributes_spec.rb +43 -0
  46. data/spec/dynamoid/document_spec.rb +38 -0
  47. data/spec/dynamoid/fields_spec.rb +26 -0
  48. data/spec/dynamoid/finders_spec.rb +114 -0
  49. data/spec/dynamoid/indexes_spec.rb +54 -0
  50. data/spec/dynamoid/persistence_spec.rb +55 -0
  51. data/spec/dynamoid/relations_spec.rb +6 -0
  52. data/spec/dynamoid_spec.rb +5 -0
  53. data/spec/spec_helper.rb +52 -0
  54. metadata +204 -0
@@ -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
@@ -0,0 +1,54 @@
1
+ require 'dynamoid/associations/association'
2
+ require 'dynamoid/associations/has_many'
3
+ require 'dynamoid/associations/belongs_to'
4
+ require 'dynamoid/associations/has_one'
5
+ require 'dynamoid/associations/has_and_belongs_to_many'
6
+
7
+ # encoding: utf-8
8
+ module Dynamoid #:nodoc:
9
+
10
+ # Connects models together through the magic of associations.
11
+ module Associations
12
+ extend ActiveSupport::Concern
13
+
14
+ included do
15
+ class_attribute :associations
16
+
17
+ self.associations = {}
18
+ end
19
+
20
+ module ClassMethods
21
+ def has_many(name, options = {})
22
+ association(:has_many, name, options)
23
+ end
24
+
25
+ def has_one(name, options = {})
26
+ association(:has_one, name, options)
27
+ end
28
+
29
+ def belongs_to(name, options = {})
30
+ association(:belongs_to, name, options)
31
+ end
32
+
33
+ def has_and_belongs_to_many(name, options = {})
34
+ association(:has_and_belongs_to_many, name, options)
35
+ end
36
+
37
+ private
38
+
39
+ def association(type, name, options = {})
40
+ field "#{name}_ids".to_sym
41
+ self.associations[name] = options.merge(:type => type)
42
+ define_method(name) do
43
+ @associations ||= {}
44
+ @associations[name] ||= Dynamoid::Associations.const_get(type.to_s.camelcase).new(self, name, options)
45
+ end
46
+ define_method("#{name}=".to_sym) do |objects|
47
+ self.send(name) << objects
48
+ end
49
+ end
50
+ end
51
+
52
+ end
53
+
54
+ end
@@ -0,0 +1,80 @@
1
+ # encoding: utf-8
2
+ module Dynamoid #:nodoc:
3
+
4
+ # The base association module.
5
+ module Associations
6
+ module Association
7
+ attr_accessor :name, :options, :source
8
+
9
+ def initialize(source, name, options)
10
+ @name = name
11
+ @options = options
12
+ @source = source
13
+ end
14
+
15
+ def empty?
16
+ records.empty?
17
+ end
18
+ alias :nil? :empty?
19
+
20
+ def size
21
+ records.count
22
+ end
23
+ alias :count :size
24
+
25
+ def include?(object)
26
+ records.include?(object)
27
+ end
28
+
29
+ def delete(object)
30
+ source.update_attribute(source_attribute, source_ids - Array(object).collect(&:id))
31
+ Array(object).collect{|o| self.send(:disassociate_target, o)} if target_association
32
+ object
33
+ end
34
+
35
+ def <<(object)
36
+ source.update_attribute(source_attribute, source_ids.merge(Array(object).collect(&:id)))
37
+ Array(object).collect{|o| self.send(:associate_target, o)} if target_association
38
+ object
39
+ end
40
+
41
+ def create(attributes = {})
42
+ object = target_class.create(attributes)
43
+ self << object
44
+ end
45
+
46
+ private
47
+
48
+ def records
49
+ results = target_class.find(source_ids.to_a)
50
+ results.nil? ? [] : Array(results)
51
+ end
52
+
53
+ def target_class
54
+ name.to_s.singularize.capitalize.constantize
55
+ end
56
+
57
+ def target_attribute
58
+ "#{target_association}_ids".to_sym if target_association
59
+ end
60
+
61
+ def target_ids
62
+ target.send(target_attribute) || Set.new
63
+ end
64
+
65
+ def source_class
66
+ source.class
67
+ end
68
+
69
+ def source_attribute
70
+ "#{name}_ids".to_sym
71
+ end
72
+
73
+ def source_ids
74
+ source.send(source_attribute) || Set.new
75
+ end
76
+
77
+ end
78
+ end
79
+
80
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+ module Dynamoid #:nodoc:
3
+
4
+ # The BelongsTo association.
5
+ module Associations
6
+ class BelongsTo
7
+ include Dynamoid::Associations::Association
8
+
9
+ def ==(other)
10
+ target == other
11
+ end
12
+
13
+ private
14
+
15
+ def target
16
+ records.first
17
+ end
18
+
19
+ def target_association
20
+ has_many_key_name = source.class.to_s.pluralize.downcase.to_sym
21
+ has_one_key_name = source.class.to_s.downcase.to_sym
22
+ if !target_class.associations[has_many_key_name].nil?
23
+ return has_many_key_name if target_class.associations[has_many_key_name][:type] == :has_many
24
+ elsif !target_class.associations[has_one_key_name].nil?
25
+ return has_one_key_name if target_class.associations[has_one_key_name][:type] == :has_one
26
+ end
27
+ end
28
+
29
+ def associate_target(object)
30
+ object.update_attribute(target_attribute, target_ids.merge(Array(source.id)))
31
+ end
32
+
33
+ def disassociate_target(object)
34
+ source.update_attribute(source_attribute, target_ids - Array(source.id))
35
+ end
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+ module Dynamoid #:nodoc:
3
+
4
+ # The habtm association.
5
+ module Associations
6
+ class HasAndBelongsToMany
7
+ include Dynamoid::Associations::Association
8
+
9
+ def ==(other)
10
+ records == Array(other)
11
+ end
12
+
13
+ private
14
+
15
+ def target_association
16
+ key_name = source.class.to_s.pluralize.downcase.to_sym
17
+ guess = target_class.associations[key_name]
18
+ return nil if guess.nil? || guess[:type] != :has_and_belongs_to_many
19
+ key_name
20
+ end
21
+
22
+ def associate_target(object)
23
+ ids = object.send(target_attribute) || Set.new
24
+ object.update_attribute(target_attribute, ids.merge(Array(source.id)))
25
+ end
26
+
27
+ def disassociate_target(object)
28
+ ids = object.send(target_attribute) || Set.new
29
+ source.update_attribute(source_attribute, ids - Array(source.id))
30
+ end
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+ module Dynamoid #:nodoc:
3
+
4
+ # The HasMany association.
5
+ module Associations
6
+ class HasMany
7
+ include Dynamoid::Associations::Association
8
+
9
+ def ==(other)
10
+ records == Array(other)
11
+ end
12
+
13
+ private
14
+
15
+ def target_association
16
+ key_name = source.class.to_s.singularize.downcase.to_sym
17
+ guess = target_class.associations[key_name]
18
+ return nil if guess.nil? || guess[:type] != :belongs_to
19
+ key_name
20
+ end
21
+
22
+ def associate_target(object)
23
+ object.update_attribute(target_attribute, Set[source.id])
24
+ end
25
+
26
+ def disassociate_target(object)
27
+ object.update_attribute(target_attribute, nil)
28
+ end
29
+
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+ module Dynamoid #:nodoc:
3
+
4
+ # The HasOne association.
5
+ module Associations
6
+ class HasOne
7
+ include Dynamoid::Associations::Association
8
+
9
+ def ==(other)
10
+ target == other
11
+ end
12
+
13
+ private
14
+
15
+ def target
16
+ records.first
17
+ end
18
+
19
+ def target_association
20
+ key_name = source.class.to_s.pluralize.downcase.to_sym
21
+ guess = target_class.associations[key_name]
22
+ return nil if guess.nil? || guess[:type] != :belongs_to
23
+ key_name
24
+ end
25
+
26
+ def associate_target(object)
27
+ object.update_attribute(target_attribute, source.id)
28
+ end
29
+
30
+ def disassociate_target(object)
31
+ source.update_attribute(source_attribute, nil)
32
+ end
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+ module Dynamoid #:nodoc:
3
+
4
+ module Attributes
5
+ extend ActiveSupport::Concern
6
+
7
+ attr_accessor :attributes
8
+ alias :raw_attributes :attributes
9
+
10
+ def write_attribute(name, value)
11
+ attributes[name.to_sym] = value
12
+ end
13
+ alias :[]= :write_attribute
14
+
15
+ def read_attribute(name)
16
+ attributes[name.to_sym]
17
+ end
18
+ alias :[] :read_attribute
19
+
20
+ def update_attributes(attributes)
21
+ self.attributes = attributes
22
+ save
23
+ end
24
+
25
+ def update_attribute(attribute, value)
26
+ self.attributes[attribute] = value
27
+ save
28
+ end
29
+
30
+ module ClassMethods
31
+ def attributes
32
+ [self.fields + [:id]].flatten.uniq
33
+ end
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+ module Dynamoid #:nodoc
3
+ module Components #:nodoc
4
+ extend ActiveSupport::Concern
5
+
6
+ # All modules that a +Document+ is composed of are defined in this
7
+ # module, to keep the document class from getting too cluttered.
8
+ included do
9
+ extend ActiveModel::Translation
10
+ end
11
+
12
+ include ActiveModel::Conversion
13
+ include ActiveModel::MassAssignmentSecurity
14
+ include ActiveModel::Naming
15
+ include ActiveModel::Observing
16
+ include ActiveModel::Serializers::JSON
17
+ include ActiveModel::Serializers::Xml
18
+ include Dynamoid::Attributes
19
+ include Dynamoid::Fields
20
+ include Dynamoid::Indexes
21
+ include Dynamoid::Persistence
22
+ include Dynamoid::Finders
23
+ include Dynamoid::Associations
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+ require "uri"
3
+ require "dynamoid/config/options"
4
+
5
+ module Dynamoid #:nodoc
6
+
7
+ module Config
8
+ extend self
9
+ extend Options
10
+ include ActiveModel::Observing
11
+
12
+ option :adapter, :default => 'local'
13
+ option :namespace, :default => 'dynamoid'
14
+ option :access_key
15
+ option :secret_key
16
+ option :warn_on_scan, :default => true
17
+
18
+ end
19
+ end