lotus-dynamodb 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.travis.yml +9 -0
- data/.yardopts +5 -0
- data/Gemfile +16 -0
- data/LICENSE.md +22 -0
- data/Procfile +1 -0
- data/README.md +112 -0
- data/Rakefile +17 -0
- data/benchmarks/coercer.rb +76 -0
- data/examples/Gemfile +2 -0
- data/examples/purchase.rb +164 -0
- data/lib/lotus/dynamodb/config.rb +14 -0
- data/lib/lotus/dynamodb/version.rb +8 -0
- data/lib/lotus/model/adapters/dynamodb/coercer.rb +211 -0
- data/lib/lotus/model/adapters/dynamodb/collection.rb +321 -0
- data/lib/lotus/model/adapters/dynamodb/command.rb +117 -0
- data/lib/lotus/model/adapters/dynamodb/query.rb +559 -0
- data/lib/lotus/model/adapters/dynamodb_adapter.rb +190 -0
- data/lib/lotus-dynamodb.rb +3 -0
- data/lotus-dynamodb.gemspec +30 -0
- data/test/fixtures.rb +75 -0
- data/test/model/adapters/dynamodb/coercer_test.rb +269 -0
- data/test/model/adapters/dynamodb/query_test.rb +259 -0
- data/test/model/adapters/dynamodb_adapter_test.rb +940 -0
- data/test/test_helper.rb +46 -0
- data/test/version_test.rb +7 -0
- metadata +203 -0
@@ -0,0 +1,190 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
require 'lotus/model/adapters/abstract'
|
3
|
+
require 'lotus/model/adapters/implementation'
|
4
|
+
require 'lotus/model/adapters/dynamodb/coercer'
|
5
|
+
require 'lotus/model/adapters/dynamodb/collection'
|
6
|
+
require 'lotus/model/adapters/dynamodb/command'
|
7
|
+
require 'lotus/model/adapters/dynamodb/query'
|
8
|
+
|
9
|
+
module Lotus
|
10
|
+
module Model
|
11
|
+
module Adapters
|
12
|
+
# Adapter for Amazon DynamoDB.
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since 0.1.0
|
16
|
+
class DynamodbAdapter < Abstract
|
17
|
+
include Implementation
|
18
|
+
|
19
|
+
# Initialize the adapter.
|
20
|
+
#
|
21
|
+
# It takes advantage of AWS::DynamoDB::Client to perform all operations.
|
22
|
+
#
|
23
|
+
# @param mapper [Object] the database mapper
|
24
|
+
#
|
25
|
+
# @return [Lotus::Model::Adapters::DynamodbAdapter]
|
26
|
+
#
|
27
|
+
# @see Lotus::Model::Mapper
|
28
|
+
# @see Lotus::Dynamodb::API_VERSION
|
29
|
+
# @see http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/DynamoDB/Client/V20120810.html
|
30
|
+
#
|
31
|
+
# @api private
|
32
|
+
# @since 0.1.0
|
33
|
+
def initialize(mapper)
|
34
|
+
super
|
35
|
+
|
36
|
+
@client = AWS::DynamoDB::Client.new(
|
37
|
+
api_version: Lotus::Dynamodb::API_VERSION
|
38
|
+
)
|
39
|
+
@collections = {}
|
40
|
+
end
|
41
|
+
|
42
|
+
# Creates a record in the database for the given entity.
|
43
|
+
# It assigns the `id` attribute, in case of success.
|
44
|
+
#
|
45
|
+
# @param collection [Symbol] the target collection (it must be mapped)
|
46
|
+
# @param entity [#id=] the entity to create
|
47
|
+
#
|
48
|
+
# @return [Object] the entity
|
49
|
+
#
|
50
|
+
# @api private
|
51
|
+
# @since 0.1.0
|
52
|
+
def create(collection, entity)
|
53
|
+
entity.id = command(collection).create(entity)
|
54
|
+
entity
|
55
|
+
end
|
56
|
+
|
57
|
+
# Updates a record in the database corresponding to the given entity.
|
58
|
+
#
|
59
|
+
# @param collection [Symbol] the target collection (it must be mapped)
|
60
|
+
# @param entity [#id] the entity to update
|
61
|
+
#
|
62
|
+
# @return [Object] the entity
|
63
|
+
#
|
64
|
+
# @api private
|
65
|
+
# @since 0.1.0
|
66
|
+
def update(collection, entity)
|
67
|
+
command(collection).update(entity)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Deletes a record in the database corresponding to the given entity.
|
71
|
+
#
|
72
|
+
# @param collection [Symbol] the target collection (it must be mapped)
|
73
|
+
# @param entity [#id] the entity to delete
|
74
|
+
#
|
75
|
+
# @api private
|
76
|
+
# @since 0.1.0
|
77
|
+
def delete(collection, entity)
|
78
|
+
command(collection).delete(entity)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Deletes all the records from the given collection.
|
82
|
+
#
|
83
|
+
# This works terribly slow at the moment, and this is only useful for
|
84
|
+
# testing small collections. Consider re-creating table from scratch.
|
85
|
+
#
|
86
|
+
# @param collection [Symbol] the target collection (it must be mapped)
|
87
|
+
#
|
88
|
+
# @api private
|
89
|
+
# @since 0.1.0
|
90
|
+
def clear(collection)
|
91
|
+
command(collection).clear
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns an unique record from the given collection, with the given
|
95
|
+
# id.
|
96
|
+
#
|
97
|
+
# @param collection [Symbol] the target collection (it must be mapped)
|
98
|
+
# @param key [Array] the identity of the object
|
99
|
+
#
|
100
|
+
# @return [Object] the entity
|
101
|
+
#
|
102
|
+
# @api private
|
103
|
+
# @since 0.1.0
|
104
|
+
def find(collection, *key)
|
105
|
+
command(collection).get(key)
|
106
|
+
end
|
107
|
+
|
108
|
+
# This method is not implemented. DynamoDB does not allow
|
109
|
+
# table-wide sorting.
|
110
|
+
#
|
111
|
+
# @see http://stackoverflow.com/a/17495069
|
112
|
+
#
|
113
|
+
# @param collection [Symbol] the target collection (it must be mapped)
|
114
|
+
#
|
115
|
+
# @raise [NotImplementedError]
|
116
|
+
#
|
117
|
+
# @since 0.1.0
|
118
|
+
def first(collection)
|
119
|
+
raise NotImplementedError
|
120
|
+
end
|
121
|
+
|
122
|
+
# This method is not implemented. DynamoDB does not allow
|
123
|
+
# table-wide sorting.
|
124
|
+
#
|
125
|
+
# @see http://stackoverflow.com/a/17495069
|
126
|
+
#
|
127
|
+
# @param collection [Symbol] the target collection (it must be mapped)
|
128
|
+
#
|
129
|
+
# @raise [NotImplementedError]
|
130
|
+
#
|
131
|
+
# @since 0.1.0
|
132
|
+
def last(collection)
|
133
|
+
raise NotImplementedError
|
134
|
+
end
|
135
|
+
|
136
|
+
# Fabricates a command for the given query.
|
137
|
+
#
|
138
|
+
# @param collection [Symbol] the target collection (it must be mapped)
|
139
|
+
#
|
140
|
+
# @return [Lotus::Model::Adapters::Dynamodb::Command]
|
141
|
+
#
|
142
|
+
# @see Lotus::Model::Adapters::Dynamodb::Command
|
143
|
+
#
|
144
|
+
# @api private
|
145
|
+
# @since 0.1.0
|
146
|
+
def command(collection)
|
147
|
+
Dynamodb::Command.new(_collection(collection), _mapped_collection(collection))
|
148
|
+
end
|
149
|
+
|
150
|
+
# Fabricates a query
|
151
|
+
#
|
152
|
+
# @param collection [Symbol] the target collection (it must be mapped)
|
153
|
+
# @param context [Object]
|
154
|
+
# @param blk [Proc] a block of code to be executed in the context of
|
155
|
+
# the query.
|
156
|
+
#
|
157
|
+
# @return [Lotus::Model::Adapters::Dynamodb::Query]
|
158
|
+
#
|
159
|
+
# @see Lotus::Model::Adapters::Dynamodb::Query
|
160
|
+
#
|
161
|
+
# @api private
|
162
|
+
# @since 0.1.0
|
163
|
+
def query(collection, context = nil, &blk)
|
164
|
+
Dynamodb::Query.new(_collection(collection), _mapped_collection(collection), &blk)
|
165
|
+
end
|
166
|
+
|
167
|
+
private
|
168
|
+
|
169
|
+
# Returns a collection from the given name.
|
170
|
+
#
|
171
|
+
# @param name [Symbol] a name of the collection (it must be mapped)
|
172
|
+
#
|
173
|
+
# @return [Lotus::Model::Adapters::Dynamodb::Collection]
|
174
|
+
#
|
175
|
+
# @see Lotus::Model::Adapters::Dynamodb::Collection
|
176
|
+
#
|
177
|
+
# @api private
|
178
|
+
# @since 0.1.0
|
179
|
+
def _collection(name)
|
180
|
+
@collections[name] ||= Dynamodb::Collection.new(
|
181
|
+
@client,
|
182
|
+
Lotus::Model::Adapters::Dynamodb::Coercer.new(_mapped_collection(name)),
|
183
|
+
name,
|
184
|
+
_identity(name),
|
185
|
+
)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'lotus/dynamodb/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'lotus-dynamodb'
|
8
|
+
spec.version = Lotus::Dynamodb::VERSION
|
9
|
+
spec.authors = ['Dmitry Krasnoukhov']
|
10
|
+
spec.email = ['dmitry@krasnoukhov.com']
|
11
|
+
spec.summary = spec.description = %q{Amazon DynamoDB adapter for Lotus::Model}
|
12
|
+
spec.homepage = 'https://github.com/krasnoukhov/lotus-dynamodb'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
|
20
|
+
spec.add_runtime_dependency 'lotus-model', '~> 0.1'
|
21
|
+
spec.add_runtime_dependency 'aws-sdk', '~> 1.0'
|
22
|
+
spec.add_runtime_dependency 'multi_json', '~> 1.10'
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.5'
|
25
|
+
spec.add_development_dependency 'minitest', '~> 5'
|
26
|
+
spec.add_development_dependency 'minitest-line', '~> 0.6'
|
27
|
+
spec.add_development_dependency 'rake', '~> 10'
|
28
|
+
spec.add_development_dependency 'fake_dynamo', '~> 0.2'
|
29
|
+
spec.add_development_dependency 'foreman', '~> 0.67'
|
30
|
+
end
|
data/test/fixtures.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
DB = AWS::DynamoDB::Client.new(api_version: Lotus::Dynamodb::API_VERSION)
|
2
|
+
|
3
|
+
begin
|
4
|
+
DB.create_table(
|
5
|
+
table_name: "test_users",
|
6
|
+
attribute_definitions: [
|
7
|
+
{ attribute_name: "id", attribute_type: "S" },
|
8
|
+
],
|
9
|
+
key_schema: [
|
10
|
+
{ attribute_name: "id", key_type: "HASH" },
|
11
|
+
],
|
12
|
+
provisioned_throughput: {
|
13
|
+
read_capacity_units: 10,
|
14
|
+
write_capacity_units: 10,
|
15
|
+
},
|
16
|
+
)
|
17
|
+
|
18
|
+
DB.create_table(
|
19
|
+
table_name: "test_devices",
|
20
|
+
attribute_definitions: [
|
21
|
+
{ attribute_name: "uuid", attribute_type: "S" },
|
22
|
+
{ attribute_name: "created_at", attribute_type: "N" },
|
23
|
+
],
|
24
|
+
key_schema: [
|
25
|
+
{ attribute_name: "uuid", key_type: "HASH" },
|
26
|
+
{ attribute_name: "created_at", key_type: "RANGE" },
|
27
|
+
],
|
28
|
+
provisioned_throughput: {
|
29
|
+
read_capacity_units: 10,
|
30
|
+
write_capacity_units: 10,
|
31
|
+
},
|
32
|
+
)
|
33
|
+
|
34
|
+
DB.create_table(
|
35
|
+
table_name: "test_purchases",
|
36
|
+
attribute_definitions: [
|
37
|
+
{ attribute_name: "region", attribute_type: "S" },
|
38
|
+
{ attribute_name: "created_at", attribute_type: "N" },
|
39
|
+
{ attribute_name: "subtotal", attribute_type: "N" },
|
40
|
+
{ attribute_name: "uuid", attribute_type: "S" },
|
41
|
+
],
|
42
|
+
key_schema: [
|
43
|
+
{ attribute_name: "region", key_type: "HASH" },
|
44
|
+
{ attribute_name: "created_at", key_type: "RANGE" },
|
45
|
+
],
|
46
|
+
local_secondary_indexes: [{
|
47
|
+
index_name: "by_subtotal",
|
48
|
+
key_schema: [
|
49
|
+
{ attribute_name: "region", key_type: "HASH" },
|
50
|
+
{ attribute_name: "subtotal", key_type: "RANGE" },
|
51
|
+
],
|
52
|
+
projection: {
|
53
|
+
projection_type: "ALL",
|
54
|
+
},
|
55
|
+
}],
|
56
|
+
global_secondary_indexes: [{
|
57
|
+
index_name: "by_uuid",
|
58
|
+
key_schema: [
|
59
|
+
{ attribute_name: "uuid", key_type: "HASH" },
|
60
|
+
],
|
61
|
+
projection: {
|
62
|
+
projection_type: "ALL",
|
63
|
+
},
|
64
|
+
provisioned_throughput: {
|
65
|
+
read_capacity_units: 10,
|
66
|
+
write_capacity_units: 10,
|
67
|
+
},
|
68
|
+
}],
|
69
|
+
provisioned_throughput: {
|
70
|
+
read_capacity_units: 10,
|
71
|
+
write_capacity_units: 10,
|
72
|
+
},
|
73
|
+
)
|
74
|
+
rescue AWS::DynamoDB::Errors::ResourceInUseException
|
75
|
+
end
|
@@ -0,0 +1,269 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'multi_json'
|
3
|
+
|
4
|
+
describe Lotus::Model::Adapters::Dynamodb::Coercer do
|
5
|
+
before do
|
6
|
+
MockEntity = Struct.new(:id) do
|
7
|
+
include Lotus::Entity
|
8
|
+
end
|
9
|
+
|
10
|
+
class MockCollection
|
11
|
+
def attributes
|
12
|
+
{ id: [Time, :id] }
|
13
|
+
end
|
14
|
+
|
15
|
+
def identity
|
16
|
+
:id
|
17
|
+
end
|
18
|
+
|
19
|
+
def entity
|
20
|
+
MockEntity
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
@coercer = Lotus::Model::Adapters::Dynamodb::Coercer.new(MockCollection.new)
|
25
|
+
end
|
26
|
+
|
27
|
+
after do
|
28
|
+
Object.send(:remove_const, :MockEntity)
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#from_*' do
|
32
|
+
describe 'skipped' do
|
33
|
+
describe 'Float' do
|
34
|
+
let(:subject) { 1.5 }
|
35
|
+
|
36
|
+
it 'remains unchanged' do
|
37
|
+
@coercer.from_float(subject).class.must_equal Float
|
38
|
+
@coercer.from_float(subject).must_equal subject
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'Integer' do
|
43
|
+
let(:subject) { 2 }
|
44
|
+
|
45
|
+
it 'remains unchanged' do
|
46
|
+
@coercer.from_integer(subject).class.must_equal Fixnum
|
47
|
+
@coercer.from_integer(subject).must_equal subject
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'Set' do
|
52
|
+
let(:subject) { Set.new([1, 2, 3]) }
|
53
|
+
|
54
|
+
it 'remains unchanged' do
|
55
|
+
@coercer.from_set(subject).class.must_equal Set
|
56
|
+
@coercer.from_set(subject).must_equal subject
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'String' do
|
61
|
+
let(:subject) { "omg" }
|
62
|
+
|
63
|
+
it 'remains unchanged' do
|
64
|
+
@coercer.from_string(subject).class.must_equal String
|
65
|
+
@coercer.from_string(subject).must_equal subject
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe 'supported' do
|
71
|
+
describe 'AWS::DynamoDB::Binary' do
|
72
|
+
let(:subject) { AWS::DynamoDB::Binary.new("HUUUGE") }
|
73
|
+
|
74
|
+
it 'coerces' do
|
75
|
+
@coercer.from_aws_dynamodb_binary(subject).class.must_equal \
|
76
|
+
AWS::DynamoDB::Binary
|
77
|
+
@coercer.from_aws_dynamodb_binary(subject).must_equal \
|
78
|
+
AWS::DynamoDB::Binary.new(subject)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe 'Array' do
|
83
|
+
let(:subject) { ["omg"] }
|
84
|
+
|
85
|
+
it 'coerces' do
|
86
|
+
@coercer.from_array(subject).class.must_equal String
|
87
|
+
@coercer.from_array(subject).must_equal MultiJson.dump(subject)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe 'Boolean' do
|
92
|
+
it 'coerces' do
|
93
|
+
@coercer.from_boolean(true).class.must_equal Fixnum
|
94
|
+
@coercer.from_boolean(true).must_equal 1
|
95
|
+
@coercer.from_boolean(false).class.must_equal Fixnum
|
96
|
+
@coercer.from_boolean(false).must_equal 0
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe 'Date' do
|
101
|
+
let(:subject) { Date.new(2014) }
|
102
|
+
|
103
|
+
it 'coerces' do
|
104
|
+
@coercer.from_date(subject).class.must_equal Fixnum
|
105
|
+
@coercer.from_date(subject).must_equal subject.to_time.to_i
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe 'DateTime' do
|
110
|
+
let(:subject) { DateTime.new(2014) }
|
111
|
+
|
112
|
+
it 'coerces' do
|
113
|
+
@coercer.from_datetime(subject).class.must_equal Float
|
114
|
+
@coercer.from_datetime(subject).must_equal subject.to_time.to_f
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe 'Hash' do
|
119
|
+
let(:subject) { { omg: "lol" } }
|
120
|
+
|
121
|
+
it 'coerces' do
|
122
|
+
@coercer.from_hash(subject).class.must_equal String
|
123
|
+
@coercer.from_hash(subject).must_equal MultiJson.dump(subject)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe 'Time' do
|
128
|
+
let(:subject) { Time.at(0) }
|
129
|
+
|
130
|
+
it 'coerces' do
|
131
|
+
@coercer.from_time(subject).class.must_equal Float
|
132
|
+
@coercer.from_time(subject).must_equal subject.to_f
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe '#to_*' do
|
139
|
+
describe 'skipped' do
|
140
|
+
describe 'Float' do
|
141
|
+
let(:subject) { 1.5 }
|
142
|
+
|
143
|
+
it 'remains unchanged' do
|
144
|
+
@coercer.to_float(subject).must_equal subject
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe 'Integer' do
|
149
|
+
let(:subject) { 2 }
|
150
|
+
|
151
|
+
it 'remains unchanged' do
|
152
|
+
@coercer.to_integer(subject).must_equal subject
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe 'Set' do
|
157
|
+
let(:subject) { Set.new([1, 2, 3]) }
|
158
|
+
|
159
|
+
it 'remains unchanged' do
|
160
|
+
@coercer.to_set(subject).class.must_equal Set
|
161
|
+
@coercer.to_set(subject).must_equal subject
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe 'String' do
|
166
|
+
let(:subject) { "omg" }
|
167
|
+
|
168
|
+
it 'remains unchanged' do
|
169
|
+
@coercer.to_string(subject).must_equal subject
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
describe 'supported' do
|
175
|
+
describe 'AWS::DynamoDB::Binary' do
|
176
|
+
let(:subject) { "HUUUGE" }
|
177
|
+
|
178
|
+
it 'coerces' do
|
179
|
+
@coercer.to_aws_dynamodb_binary(subject).class.must_equal \
|
180
|
+
AWS::DynamoDB::Binary
|
181
|
+
@coercer.to_aws_dynamodb_binary(subject).must_equal \
|
182
|
+
AWS::DynamoDB::Binary.new(subject)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe 'Array' do
|
187
|
+
let(:subject) { MultiJson.dump(["omg"]) }
|
188
|
+
|
189
|
+
it 'coerces' do
|
190
|
+
@coercer.to_array(subject).class.must_equal Array
|
191
|
+
@coercer.to_array(subject).must_equal MultiJson.load(subject)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
describe 'Boolean' do
|
196
|
+
it 'coerces' do
|
197
|
+
@coercer.to_boolean(1).class.must_equal TrueClass
|
198
|
+
@coercer.to_boolean(1).must_equal true
|
199
|
+
@coercer.to_boolean(0).class.must_equal FalseClass
|
200
|
+
@coercer.to_boolean(0).must_equal false
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe 'Date' do
|
205
|
+
let(:subject) { Date.new(2014) }
|
206
|
+
|
207
|
+
it 'coerces' do
|
208
|
+
@coercer.to_date(subject.to_time.to_i).class.must_equal Date
|
209
|
+
@coercer.to_date(subject.to_time.to_i).must_equal subject
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe 'DateTime' do
|
214
|
+
let(:subject) { DateTime.new(2014) }
|
215
|
+
|
216
|
+
it 'coerces' do
|
217
|
+
@coercer.to_datetime(subject.to_time.to_f).class.must_equal DateTime
|
218
|
+
@coercer.to_datetime(subject.to_time.to_f).must_equal subject
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
describe 'Hash' do
|
223
|
+
let(:subject) { MultiJson.dump({ omg: "lol" }) }
|
224
|
+
|
225
|
+
it 'coerces' do
|
226
|
+
@coercer.to_hash(subject).class.must_equal Hash
|
227
|
+
@coercer.to_hash(subject).must_equal MultiJson.load(subject)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
describe 'Time' do
|
232
|
+
let(:subject) { Time.at(0) }
|
233
|
+
|
234
|
+
it 'coerces' do
|
235
|
+
@coercer.to_time(subject.to_f).class.must_equal Time
|
236
|
+
@coercer.to_time(subject.to_f).must_equal subject
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
describe '#deserialize_*' do
|
243
|
+
it 'deserializes id' do
|
244
|
+
@coercer.deserialize_id(0.0).must_equal Time.at(0)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
describe '#serialize_*' do
|
249
|
+
it 'serializes id' do
|
250
|
+
@coercer.serialize_id(Time.at(0)).must_equal 0.0
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
describe '#to_record' do
|
255
|
+
let(:subject) { MockEntity.new(id: Time.at(0)) }
|
256
|
+
|
257
|
+
it 'serializes entity' do
|
258
|
+
@coercer.to_record(subject).must_equal ({ id: 0.0 })
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
describe '#from_record' do
|
263
|
+
let(:subject) { { id: 1.0 } }
|
264
|
+
|
265
|
+
it 'deserializes entity' do
|
266
|
+
@coercer.from_record(subject).must_equal MockEntity.new(id: Time.at(1))
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|