dynamoid 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Dynamoid.gemspec +2 -6
- data/README.markdown +28 -4
- data/VERSION +1 -1
- data/lib/dynamoid.rb +4 -1
- data/lib/dynamoid/adapter.rb +79 -6
- data/lib/dynamoid/adapter/aws_sdk.rb +9 -7
- data/lib/dynamoid/adapter/local.rb +1 -1
- data/lib/dynamoid/associations.rb +1 -1
- data/lib/dynamoid/associations/association.rb +26 -4
- data/lib/dynamoid/associations/belongs_to.rb +8 -0
- data/lib/dynamoid/components.rb +3 -1
- data/lib/dynamoid/config.rb +3 -0
- data/lib/dynamoid/criteria/chain.rb +1 -1
- data/lib/dynamoid/document.rb +18 -15
- data/lib/dynamoid/fields.rb +39 -4
- data/lib/dynamoid/finders.rb +3 -3
- data/lib/dynamoid/indexes.rb +10 -17
- data/lib/dynamoid/persistence.rb +92 -16
- data/spec/app/models/magazine.rb +2 -0
- data/spec/app/models/subscription.rb +2 -0
- data/spec/dynamoid/adapter_spec.rb +76 -0
- data/spec/dynamoid/associations/association_spec.rb +22 -0
- data/spec/dynamoid/associations/belongs_to_spec.rb +8 -0
- data/spec/dynamoid/criteria_spec.rb +3 -0
- data/spec/dynamoid/document_spec.rb +11 -2
- data/spec/dynamoid/fields_spec.rb +57 -0
- data/spec/dynamoid/indexes_spec.rb +7 -15
- data/spec/dynamoid/persistence_spec.rb +34 -4
- data/spec/spec_helper.rb +5 -5
- metadata +21 -25
- data/lib/dynamoid/attributes.rb +0 -37
- data/lib/dynamoid/relations.rb +0 -21
- data/spec/dynamoid/attributes_spec.rb +0 -46
- data/spec/dynamoid/relations_spec.rb +0 -6
data/Dynamoid.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "dynamoid"
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Josh Symonds"]
|
12
|
-
s.date = "2012-
|
12
|
+
s.date = "2012-03-05"
|
13
13
|
s.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."
|
14
14
|
s.email = "josh@joshsymonds.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -36,7 +36,6 @@ Gem::Specification.new do |s|
|
|
36
36
|
"lib/dynamoid/associations/has_and_belongs_to_many.rb",
|
37
37
|
"lib/dynamoid/associations/has_many.rb",
|
38
38
|
"lib/dynamoid/associations/has_one.rb",
|
39
|
-
"lib/dynamoid/attributes.rb",
|
40
39
|
"lib/dynamoid/components.rb",
|
41
40
|
"lib/dynamoid/config.rb",
|
42
41
|
"lib/dynamoid/config/options.rb",
|
@@ -48,7 +47,6 @@ Gem::Specification.new do |s|
|
|
48
47
|
"lib/dynamoid/finders.rb",
|
49
48
|
"lib/dynamoid/indexes.rb",
|
50
49
|
"lib/dynamoid/persistence.rb",
|
51
|
-
"lib/dynamoid/relations.rb",
|
52
50
|
"spec/app/models/address.rb",
|
53
51
|
"spec/app/models/magazine.rb",
|
54
52
|
"spec/app/models/sponsor.rb",
|
@@ -63,7 +61,6 @@ Gem::Specification.new do |s|
|
|
63
61
|
"spec/dynamoid/associations/has_many_spec.rb",
|
64
62
|
"spec/dynamoid/associations/has_one_spec.rb",
|
65
63
|
"spec/dynamoid/associations_spec.rb",
|
66
|
-
"spec/dynamoid/attributes_spec.rb",
|
67
64
|
"spec/dynamoid/config_spec.rb",
|
68
65
|
"spec/dynamoid/criteria/chain_spec.rb",
|
69
66
|
"spec/dynamoid/criteria_spec.rb",
|
@@ -72,7 +69,6 @@ Gem::Specification.new do |s|
|
|
72
69
|
"spec/dynamoid/finders_spec.rb",
|
73
70
|
"spec/dynamoid/indexes_spec.rb",
|
74
71
|
"spec/dynamoid/persistence_spec.rb",
|
75
|
-
"spec/dynamoid/relations_spec.rb",
|
76
72
|
"spec/dynamoid_spec.rb",
|
77
73
|
"spec/spec_helper.rb"
|
78
74
|
]
|
data/README.markdown
CHANGED
@@ -2,6 +2,10 @@
|
|
2
2
|
|
3
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
4
|
|
5
|
+
DynamoDB is not like other document-based databases you might know, and is very different indeed from relational databases. It sacrifices anything beyond the simplest relational queries and transactional support to provide a fast, cost-efficient, and highly durable storage solution. If your database requires complicated relational queries and transaction support, then this modest Gem cannot provide them for you, and neither can DynamoDB. In those cases you would do better to look elsewhere for your database needs.
|
6
|
+
|
7
|
+
But if you want a fast, scalable, simple, easy-to-use database (and a Gem that supports it) then look no further!
|
8
|
+
|
5
9
|
## Warning!
|
6
10
|
|
7
11
|
I'm still working on this gem a lot. It only provides .where(arguments) in its criteria chaining so far. More is coming though!
|
@@ -18,12 +22,14 @@ Then you need to initialize it to get it going. Put code similar to this somewhe
|
|
18
22
|
|
19
23
|
```ruby
|
20
24
|
Dynamoid.configure do |config|
|
21
|
-
config.adapter = 'local' # This adapter allows offline development without connecting to the DynamoDB servers. Data is NOT persisted.
|
22
|
-
# config.adapter = 'aws_sdk' # This adapter establishes a connection to the DynamoDB servers using
|
25
|
+
config.adapter = 'local' # This adapter allows offline development without connecting to the DynamoDB servers. Data is *NOT* persisted.
|
26
|
+
# config.adapter = 'aws_sdk' # This adapter establishes a connection to the DynamoDB servers using Amazon's own AWS gem.
|
23
27
|
# config.access_key = 'access_key' # If connecting to DynamoDB, your access key is required.
|
24
28
|
# config.secret_key = 'secret_key' # So is your secret key.
|
25
29
|
config.namespace = "dynamoid_#{Rails.application.class.parent_name}_#{Rails.env}" # To namespace tables created by Dynamoid from other tables you might have.
|
26
|
-
config.warn_on_scan = true # Output a warning to
|
30
|
+
config.warn_on_scan = true # Output a warning to the logger when you perform a scan rather than a query on a table.
|
31
|
+
config.partitioning = true # Spread writes randomly across the database. See "partitioning" below for more.
|
32
|
+
config.partition_size = 200 # Determine the key space size that writes are randomly spread across.
|
27
33
|
end
|
28
34
|
|
29
35
|
```
|
@@ -36,9 +42,11 @@ class User
|
|
36
42
|
|
37
43
|
field :name # Every field you have on the object must be specified here.
|
38
44
|
field :email # If you have fields that aren't specified they won't be attached to the object as methods.
|
45
|
+
field :rank, :integer # Every field is assumed to be a string unless otherwise specified.
|
46
|
+
# created_at and updated_at with a type of :datetime are automatically added.
|
39
47
|
|
40
48
|
index :name # Only specify indexes if you intend to perform queries on the specified fields.
|
41
|
-
index :email # Fields without indexes
|
49
|
+
index :email # Fields without indexes suffer extremely poor performance as they must use
|
42
50
|
index [:name, :email] # scan rather than query.
|
43
51
|
|
44
52
|
has_many :addresses # Associations do not accept any options presently. The referenced
|
@@ -84,6 +92,22 @@ Address.where(:city => 'Chicago').all # Find by any number of matching criteria.
|
|
84
92
|
Address.find_by_city('Chicago') # The same as above, but using ActiveRecord's older syntax.
|
85
93
|
```
|
86
94
|
|
95
|
+
And you can also query on associations:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
u.addresses.where(:city => 'Chicago').all
|
99
|
+
```
|
100
|
+
|
101
|
+
But keep in mind Dynamoid -- and document-based storage systems in general -- are not drop-in replacements for existing relational databases. The above query does not efficiently perform a conditional join, but instead finds all the user's addresses and naively filters them in Ruby. For large associations this is a performance hit compared to relational database engines.
|
102
|
+
|
103
|
+
## Partitioning, Provisioning, and Performance
|
104
|
+
|
105
|
+
DynamoDB achieves much of its speed by relying on a random pattern of writes and reads: internally, hash keys are distributed across servers, and reading from two consecutive servers is much faster than reading from the same server twice. Of course, many of our applications request one key (like a commonly used role, a superuser, or a very popular product) much more frequently than other keys. In DynamoDB, this will result in lowered throughput and slower response times, and is a design pattern we should try to avoid.
|
106
|
+
|
107
|
+
Dynamoid attempts to obviate this problem transparently by employing a partitioning strategy to divide up keys randomly across DynamoDB's servers. Each ID is assigned an additional number (by default 0 to 199, but you can increase the partition size in Dynamoid's configuration) upon save; when read, all 200 hashes are retrieved simultaneously and the most recently updated one is returned to the application. This results in a significant net performance increase, and is usually invisible to the application itself. It does, however, bring up the important issue of provisioning your DynamoDB tables correctly.
|
108
|
+
|
109
|
+
When your read or write provisioning exceed your table's allowed throughput, DynamoDB will wait on connections until throughput is available again. This will appear as very, very slow requests and can be somewhat frustrating. Partitioning significantly increases the amount of throughput tables will experience; though DynamoDB will ignore keys that don't exist, if you have 20 partitioned keys representing one object, all will be retrieved every time the object is requested. Ensure that your tables are set up for this kind of throughput, or turn provisioning off, to make sure that DynamoDB doesn't throttle your requests.
|
110
|
+
|
87
111
|
## Credits
|
88
112
|
|
89
113
|
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.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.2
|
data/lib/dynamoid.rb
CHANGED
@@ -9,7 +9,6 @@ require "active_support/time_with_zone"
|
|
9
9
|
require "active_model"
|
10
10
|
|
11
11
|
require 'dynamoid/errors'
|
12
|
-
require 'dynamoid/attributes'
|
13
12
|
require 'dynamoid/fields'
|
14
13
|
require 'dynamoid/indexes'
|
15
14
|
require 'dynamoid/associations'
|
@@ -34,4 +33,8 @@ module Dynamoid
|
|
34
33
|
Dynamoid::Config.logger
|
35
34
|
end
|
36
35
|
|
36
|
+
def included_models
|
37
|
+
@included_models ||= []
|
38
|
+
end
|
39
|
+
|
37
40
|
end
|
data/lib/dynamoid/adapter.rb
CHANGED
@@ -3,6 +3,7 @@ module Dynamoid #:nodoc:
|
|
3
3
|
|
4
4
|
module Adapter
|
5
5
|
extend self
|
6
|
+
attr_accessor :tables
|
6
7
|
|
7
8
|
def adapter
|
8
9
|
reconnect! unless @adapter
|
@@ -13,17 +14,89 @@ module Dynamoid #:nodoc:
|
|
13
14
|
require "dynamoid/adapter/#{Dynamoid::Config.adapter}" unless Dynamoid::Adapter.const_defined?(Dynamoid::Config.adapter.camelcase)
|
14
15
|
@adapter = Dynamoid::Adapter.const_get(Dynamoid::Config.adapter.camelcase)
|
15
16
|
@adapter.connect! if @adapter.respond_to?(:connect!)
|
17
|
+
self.tables = benchmark('Cache Tables') {list_tables}
|
16
18
|
end
|
17
19
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
def benchmark(method, *args)
|
21
|
+
start = Time.now
|
22
|
+
result = yield
|
23
|
+
Dynamoid.logger.info "(#{((Time.now - start) * 1000.0).round(2)} ms) #{method.to_s.split('_').collect(&:upcase).join(' ')}#{ " - #{args.inspect}" unless args.nil? || args.empty? }"
|
24
|
+
return result
|
25
|
+
end
|
26
|
+
|
27
|
+
def write(table, object)
|
28
|
+
if Dynamoid::Config.partitioning? && object[:id]
|
29
|
+
object[:id] = "#{object[:id]}.#{Random.rand(Dynamoid::Config.partition_size)}"
|
30
|
+
object[:updated_at] = Time.now.to_f
|
31
|
+
end
|
32
|
+
benchmark('Put Item', object) {put_item(table, object)}
|
33
|
+
end
|
34
|
+
|
35
|
+
def read(table, ids)
|
36
|
+
if ids.respond_to?(:each)
|
37
|
+
if Dynamoid::Config.partitioning?
|
38
|
+
results = benchmark('Partitioned Batch Get Item', ids) {batch_get_item(table => id_with_partitions(ids))}
|
39
|
+
{table => result_for_partition(results[table])}
|
40
|
+
else
|
41
|
+
benchmark('Batch Get Item', ids) {batch_get_item(table => ids)}
|
42
|
+
end
|
43
|
+
else
|
44
|
+
if Dynamoid::Config.partitioning?
|
45
|
+
results = benchmark('Partitioned Get Item', ids) {batch_get_item(table => id_with_partitions(ids))}
|
46
|
+
result_for_partition(results[table]).first
|
47
|
+
else
|
48
|
+
benchmark('Get Item', ids) {get_item(table, ids)}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def delete(table, id)
|
54
|
+
if Dynamoid::Config.partitioning?
|
55
|
+
benchmark('Delete Item', id) do
|
56
|
+
id_with_partitions(id).each {|i| delete_item(table, i)}
|
57
|
+
end
|
58
|
+
else
|
59
|
+
benchmark('Delete Item', id) {delete_item(table, id)}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def scan(table, query)
|
64
|
+
if Dynamoid::Config.partitioning?
|
65
|
+
results = benchmark('Scan', table, query) {adapter.scan(table, query)}
|
66
|
+
result_for_partition(results)
|
67
|
+
else
|
68
|
+
adapter.scan(table, query)
|
24
69
|
end
|
70
|
+
end
|
71
|
+
|
72
|
+
[:batch_get_item, :create_table, :delete_item, :delete_table, :get_item, :list_tables, :put_item, :query].each do |m|
|
73
|
+
define_method(m) do |*args|
|
74
|
+
benchmark("#{m.to_s}", args) {adapter.send(m, *args)}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def id_with_partitions(ids)
|
79
|
+
Array(ids).collect {|id| (0...Dynamoid::Config.partition_size).collect{|n| "#{id}.#{n}"}}.flatten
|
80
|
+
end
|
81
|
+
|
82
|
+
def result_for_partition(results)
|
83
|
+
Hash.new.tap do |hash|
|
84
|
+
Array(results).each do |result|
|
85
|
+
next if result.nil?
|
86
|
+
id = result[:id].split('.').first
|
87
|
+
if !hash[id] || (result[:updated_at] > hash[id][:updated_at])
|
88
|
+
result[:id] = id
|
89
|
+
hash[id] = result
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end.values
|
93
|
+
end
|
94
|
+
|
95
|
+
def method_missing(method, *args)
|
96
|
+
return benchmark(method, *args) {adapter.send(method, *args)} if @adapter.respond_to?(method)
|
25
97
|
super
|
26
98
|
end
|
99
|
+
|
27
100
|
end
|
28
101
|
|
29
102
|
end
|
@@ -16,21 +16,23 @@ module Dynamoid
|
|
16
16
|
|
17
17
|
# BatchGetItem
|
18
18
|
def batch_get_item(options)
|
19
|
-
batch = AWS::DynamoDB::BatchGet.new(:config => @@connection.config)
|
20
19
|
hash = Hash.new{|h, k| h[k] = []}
|
21
20
|
return hash if options.all?{|k, v| v.empty?}
|
22
21
|
options.each do |t, ids|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
Array(ids).in_groups_of(100, false) do |group|
|
23
|
+
batch = AWS::DynamoDB::BatchGet.new(:config => @@connection.config)
|
24
|
+
batch.table(t, :all, Array(group)) unless group.nil? || group.empty?
|
25
|
+
batch.each do |table_name, attributes|
|
26
|
+
hash[table_name] << attributes.symbolize_keys!
|
27
|
+
end
|
28
|
+
end
|
27
29
|
end
|
28
30
|
hash
|
29
31
|
end
|
30
32
|
|
31
33
|
# CreateTable
|
32
34
|
def create_table(table_name, key)
|
33
|
-
table = @@connection.tables.create(table_name,
|
35
|
+
table = @@connection.tables.create(table_name, 100, 20, :hash_key => {key.to_sym => :string})
|
34
36
|
sleep 0.5 while table.status == :creating
|
35
37
|
return table
|
36
38
|
end
|
@@ -72,7 +74,7 @@ module Dynamoid
|
|
72
74
|
def put_item(table_name, object)
|
73
75
|
table = @@connection.tables[table_name]
|
74
76
|
table.load_schema
|
75
|
-
table.items.create(object.delete_if{|k, v| v.nil? || v.empty?})
|
77
|
+
table.items.create(object.delete_if{|k, v| v.nil? || (v.respond_to?(:empty?) && v.empty?)})
|
76
78
|
end
|
77
79
|
|
78
80
|
# Query
|
@@ -54,7 +54,7 @@ module Dynamoid
|
|
54
54
|
# PutItem
|
55
55
|
def put_item(table_name, object)
|
56
56
|
table = data[table_name]
|
57
|
-
table[:data][object[table[:id]]] = object
|
57
|
+
table[:data][object[table[:id]]] = object.delete_if{|k, v| v.nil? || (v.respond_to?(:empty?) && v.empty?)}
|
58
58
|
end
|
59
59
|
|
60
60
|
# Query
|
@@ -4,14 +4,24 @@ module Dynamoid #:nodoc:
|
|
4
4
|
# The base association module.
|
5
5
|
module Associations
|
6
6
|
module Association
|
7
|
-
attr_accessor :name, :options, :source
|
7
|
+
attr_accessor :name, :options, :source, :query
|
8
|
+
include Enumerable
|
8
9
|
|
9
10
|
def initialize(source, name, options)
|
10
11
|
@name = name
|
11
12
|
@options = options
|
12
13
|
@source = source
|
14
|
+
@query = {}
|
13
15
|
end
|
14
16
|
|
17
|
+
def records
|
18
|
+
results = target_class.find(source_ids.to_a)
|
19
|
+
results = results.nil? ? [] : Array(results)
|
20
|
+
return results if query.empty?
|
21
|
+
results_with_query(results)
|
22
|
+
end
|
23
|
+
alias :all :records
|
24
|
+
|
15
25
|
def empty?
|
16
26
|
records.empty?
|
17
27
|
end
|
@@ -49,11 +59,23 @@ module Dynamoid #:nodoc:
|
|
49
59
|
self << object
|
50
60
|
end
|
51
61
|
|
62
|
+
def where(args)
|
63
|
+
args.each {|k, v| query[k] = v}
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
def each(&block)
|
68
|
+
records.each(&block)
|
69
|
+
end
|
70
|
+
|
52
71
|
private
|
53
72
|
|
54
|
-
def
|
55
|
-
results
|
56
|
-
|
73
|
+
def results_with_query(results)
|
74
|
+
results.find_all do |result|
|
75
|
+
query.all? do |attribute, value|
|
76
|
+
result.send(attribute) == value
|
77
|
+
end
|
78
|
+
end
|
57
79
|
end
|
58
80
|
|
59
81
|
def target_class
|
data/lib/dynamoid/components.rb
CHANGED
@@ -10,6 +10,9 @@ module Dynamoid #:nodoc
|
|
10
10
|
extend ActiveModel::Callbacks
|
11
11
|
|
12
12
|
define_model_callbacks :create, :save, :destroy
|
13
|
+
|
14
|
+
before_create :set_created_at
|
15
|
+
before_save :set_updated_at
|
13
16
|
end
|
14
17
|
|
15
18
|
include ActiveModel::Conversion
|
@@ -19,7 +22,6 @@ module Dynamoid #:nodoc
|
|
19
22
|
include ActiveModel::Validations
|
20
23
|
include ActiveModel::Serializers::JSON
|
21
24
|
include ActiveModel::Serializers::Xml
|
22
|
-
include Dynamoid::Attributes
|
23
25
|
include Dynamoid::Fields
|
24
26
|
include Dynamoid::Indexes
|
25
27
|
include Dynamoid::Persistence
|
data/lib/dynamoid/config.rb
CHANGED
@@ -15,6 +15,9 @@ module Dynamoid #:nodoc
|
|
15
15
|
option :access_key
|
16
16
|
option :secret_key
|
17
17
|
option :warn_on_scan, :default => true
|
18
|
+
option :partitioning, :default => false
|
19
|
+
option :partition_size, :default => 200
|
20
|
+
option :included_models, :default => []
|
18
21
|
|
19
22
|
def default_logger
|
20
23
|
defined?(Rails) && Rails.respond_to?(:logger) ? Rails.logger : ::Logger.new($stdout)
|
@@ -38,7 +38,7 @@ module Dynamoid #:nodoc:
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def records_with_index
|
41
|
-
ids = Dynamoid::Adapter.
|
41
|
+
ids = Dynamoid::Adapter.read(source.index_table_name(index), source.key_for_index(index, values_for_index))
|
42
42
|
if ids.nil? || ids.empty?
|
43
43
|
[]
|
44
44
|
else
|
data/lib/dynamoid/document.rb
CHANGED
@@ -6,22 +6,9 @@ module Dynamoid #:nodoc:
|
|
6
6
|
module Document
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
include Dynamoid::Components
|
9
|
-
|
10
|
-
attr_accessor :new_record
|
11
|
-
alias :new_record? :new_record
|
12
|
-
|
13
|
-
def initialize(attrs = {})
|
14
|
-
@new_record = true
|
15
|
-
@attributes ||= {}
|
16
|
-
self.class.attributes.each {|att| write_attribute(att, attrs[att])}
|
17
|
-
end
|
18
9
|
|
19
|
-
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
def ==(other)
|
24
|
-
other.respond_to?(:id) && other.id == self.id
|
10
|
+
included do
|
11
|
+
Dynamoid::Config.included_models << self
|
25
12
|
end
|
26
13
|
|
27
14
|
module ClassMethods
|
@@ -37,6 +24,22 @@ module Dynamoid #:nodoc:
|
|
37
24
|
self.new(attrs)
|
38
25
|
end
|
39
26
|
end
|
27
|
+
|
28
|
+
def initialize(attrs = {})
|
29
|
+
@new_record = true
|
30
|
+
@attributes ||= {}
|
31
|
+
attrs = self.class.undump(attrs)
|
32
|
+
self.class.attributes.keys.each {|att| write_attribute(att, attrs[att])}
|
33
|
+
end
|
34
|
+
|
35
|
+
def ==(other)
|
36
|
+
other.respond_to?(:id) && other.id == self.id
|
37
|
+
end
|
38
|
+
|
39
|
+
def reload
|
40
|
+
self.attributes = self.class.find(self.id).attributes
|
41
|
+
self
|
42
|
+
end
|
40
43
|
end
|
41
44
|
|
42
45
|
end
|
data/lib/dynamoid/fields.rb
CHANGED
@@ -5,16 +5,18 @@ module Dynamoid #:nodoc:
|
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
included do
|
8
|
-
class_attribute :
|
8
|
+
class_attribute :attributes
|
9
9
|
|
10
|
-
self.
|
10
|
+
self.attributes = {}
|
11
11
|
field :id
|
12
|
+
field :created_at, :datetime
|
13
|
+
field :updated_at, :datetime
|
12
14
|
end
|
13
15
|
|
14
16
|
module ClassMethods
|
15
|
-
def field(name, options = {})
|
17
|
+
def field(name, type = :string, options = {})
|
16
18
|
named = name.to_s
|
17
|
-
self.
|
19
|
+
self.attributes[name] = {:type => type}.merge(options)
|
18
20
|
define_method(named) do
|
19
21
|
read_attribute(named)
|
20
22
|
end
|
@@ -27,6 +29,39 @@ module Dynamoid #:nodoc:
|
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
32
|
+
attr_accessor :attributes
|
33
|
+
alias :raw_attributes :attributes
|
34
|
+
|
35
|
+
def write_attribute(name, value)
|
36
|
+
attributes[name.to_sym] = value
|
37
|
+
end
|
38
|
+
alias :[]= :write_attribute
|
39
|
+
|
40
|
+
def read_attribute(name)
|
41
|
+
attributes[name.to_sym]
|
42
|
+
end
|
43
|
+
alias :[] :read_attribute
|
44
|
+
|
45
|
+
def update_attributes(attributes)
|
46
|
+
attributes.each {|attribute, value| self.write_attribute(attribute, value)}
|
47
|
+
save
|
48
|
+
end
|
49
|
+
|
50
|
+
def update_attribute(attribute, value)
|
51
|
+
write_attribute(attribute, value)
|
52
|
+
save
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def set_created_at
|
58
|
+
self.created_at = DateTime.now
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_updated_at
|
62
|
+
self.updated_at = DateTime.now
|
63
|
+
end
|
64
|
+
|
30
65
|
end
|
31
66
|
|
32
67
|
end
|