aws-sdk 1.2.6 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/aws.rb +2 -0
- data/lib/aws/api_config/DynamoDB-2011-12-05.yml +721 -0
- data/lib/aws/core.rb +10 -1
- data/lib/aws/core/client.rb +17 -12
- data/lib/aws/core/configuration.rb +13 -3
- data/lib/aws/core/configured_json_client_methods.rb +71 -0
- data/lib/aws/core/lazy_error_classes.rb +7 -2
- data/lib/aws/core/option_grammar.rb +67 -13
- data/lib/aws/core/resource.rb +9 -1
- data/lib/aws/core/session_signer.rb +95 -0
- data/lib/aws/dynamo_db.rb +169 -0
- data/lib/aws/dynamo_db/attribute_collection.rb +460 -0
- data/lib/aws/dynamo_db/batch_get.rb +206 -0
- data/lib/aws/dynamo_db/client.rb +119 -0
- data/lib/aws/dynamo_db/config.rb +20 -0
- data/lib/aws/dynamo_db/errors.rb +57 -0
- data/lib/aws/dynamo_db/expectations.rb +40 -0
- data/lib/aws/dynamo_db/item.rb +130 -0
- data/lib/aws/dynamo_db/item_collection.rb +837 -0
- data/lib/aws/{record/optimistic_locking.rb → dynamo_db/item_data.rb} +9 -12
- data/lib/aws/{record/attributes/boolean.rb → dynamo_db/keys.rb} +15 -23
- data/lib/aws/dynamo_db/primary_key_element.rb +47 -0
- data/lib/aws/dynamo_db/request.rb +78 -0
- data/lib/aws/{record/attributes/float.rb → dynamo_db/resource.rb} +10 -25
- data/lib/aws/dynamo_db/table.rb +418 -0
- data/lib/aws/dynamo_db/table_collection.rb +165 -0
- data/lib/aws/dynamo_db/types.rb +86 -0
- data/lib/aws/ec2/resource_tag_collection.rb +3 -1
- data/lib/aws/record.rb +36 -8
- data/lib/aws/record/abstract_base.rb +642 -0
- data/lib/aws/record/attributes.rb +384 -0
- data/lib/aws/record/dirty_tracking.rb +0 -1
- data/lib/aws/record/errors.rb +0 -8
- data/lib/aws/record/hash_model.rb +163 -0
- data/lib/aws/record/hash_model/attributes.rb +182 -0
- data/lib/aws/record/hash_model/finder_methods.rb +178 -0
- data/lib/aws/record/hash_model/scope.rb +108 -0
- data/lib/aws/record/model.rb +429 -0
- data/lib/aws/record/model/attributes.rb +377 -0
- data/lib/aws/record/model/finder_methods.rb +232 -0
- data/lib/aws/record/model/scope.rb +213 -0
- data/lib/aws/record/scope.rb +43 -169
- data/lib/aws/record/validations.rb +11 -11
- data/lib/aws/s3/client.rb +9 -6
- data/lib/aws/s3/object_collection.rb +1 -1
- data/lib/aws/simple_db/expect_condition_option.rb +1 -1
- data/lib/aws/simple_db/item_collection.rb +5 -3
- data/lib/aws/sts/client.rb +9 -0
- metadata +73 -30
- data/lib/aws/record/attribute.rb +0 -94
- data/lib/aws/record/attribute_macros.rb +0 -312
- data/lib/aws/record/attributes/date.rb +0 -89
- data/lib/aws/record/attributes/datetime.rb +0 -86
- data/lib/aws/record/attributes/integer.rb +0 -68
- data/lib/aws/record/attributes/sortable_float.rb +0 -60
- data/lib/aws/record/attributes/sortable_integer.rb +0 -95
- data/lib/aws/record/attributes/string.rb +0 -69
- data/lib/aws/record/base.rb +0 -828
- data/lib/aws/record/finder_methods.rb +0 -230
- data/lib/aws/record/scopes.rb +0 -55
@@ -1,68 +0,0 @@
|
|
1
|
-
# Copyright 2011-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
4
|
-
# may not use this file except in compliance with the License. A copy of
|
5
|
-
# the License is located at
|
6
|
-
#
|
7
|
-
# http://aws.amazon.com/apache2.0/
|
8
|
-
#
|
9
|
-
# or in the "license" file accompanying this file. This file is
|
10
|
-
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
11
|
-
# ANY KIND, either express or implied. See the License for the specific
|
12
|
-
# language governing permissions and limitations under the License.
|
13
|
-
|
14
|
-
require 'aws/record/attribute'
|
15
|
-
|
16
|
-
module AWS
|
17
|
-
module Record
|
18
|
-
|
19
|
-
# @private
|
20
|
-
class IntegerAttribute < Attribute
|
21
|
-
|
22
|
-
# Returns value cast to an integer. Empty strings are cast to
|
23
|
-
# nil by default. Type casting is done by calling #to_i on the value.
|
24
|
-
#
|
25
|
-
# int_attribute.type_cast('123')
|
26
|
-
# #=> 123
|
27
|
-
#
|
28
|
-
# int_attribute.type_cast('')
|
29
|
-
# #=> nil
|
30
|
-
#
|
31
|
-
# @param [Mixed] value The value to type cast to an integer.
|
32
|
-
# @return [Integer,nil] Returns the type casted integer or nil
|
33
|
-
def self.type_cast raw_value, options = {}
|
34
|
-
case raw_value
|
35
|
-
when nil then nil
|
36
|
-
when '' then nil
|
37
|
-
when Integer then raw_value
|
38
|
-
else
|
39
|
-
raw_value.respond_to?(:to_i) ?
|
40
|
-
raw_value.to_i :
|
41
|
-
raw_value.to_s.to_i
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
# Returns a serialized representation of the integer value suitable for
|
46
|
-
# storing in SimpleDB.
|
47
|
-
#
|
48
|
-
# attribute.serialize(123)
|
49
|
-
# #=> '123'
|
50
|
-
#
|
51
|
-
# @param [Integer] integer The number to serialize.
|
52
|
-
# @param [Hash] options
|
53
|
-
# @return [String] A serialized representation of the integer.
|
54
|
-
def self.serialize integer, options = {}
|
55
|
-
expect(Integer, integer) do
|
56
|
-
integer.to_s
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
# @private
|
61
|
-
def self.allow_set?
|
62
|
-
true
|
63
|
-
end
|
64
|
-
|
65
|
-
end
|
66
|
-
|
67
|
-
end
|
68
|
-
end
|
@@ -1,60 +0,0 @@
|
|
1
|
-
# Copyright 2011-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
4
|
-
# may not use this file except in compliance with the License. A copy of
|
5
|
-
# the License is located at
|
6
|
-
#
|
7
|
-
# http://aws.amazon.com/apache2.0/
|
8
|
-
#
|
9
|
-
# or in the "license" file accompanying this file. This file is
|
10
|
-
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
11
|
-
# ANY KIND, either express or implied. See the License for the specific
|
12
|
-
# language governing permissions and limitations under the License.
|
13
|
-
|
14
|
-
require 'aws/record/attribute'
|
15
|
-
|
16
|
-
module AWS
|
17
|
-
module Record
|
18
|
-
|
19
|
-
# @private
|
20
|
-
class SortableFloatAttribute < FloatAttribute
|
21
|
-
|
22
|
-
def initialize name, options = {}
|
23
|
-
|
24
|
-
range = options[:range]
|
25
|
-
raise ArgumentError, "missing required option :range" unless range
|
26
|
-
raise ArgumentError, ":range should be an integer range" unless
|
27
|
-
range.is_a?(Range) and range.first.is_a?(Integer)
|
28
|
-
|
29
|
-
super(name, options)
|
30
|
-
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.serialize float, options = {}
|
34
|
-
expect(Float, float) do
|
35
|
-
|
36
|
-
left, right = float.to_s.split('.')
|
37
|
-
|
38
|
-
left = SortableIntegerAttribute.serialize(left.to_i, options)
|
39
|
-
|
40
|
-
SortableIntegerAttribute.check_range(float, options)
|
41
|
-
|
42
|
-
"#{left}.#{right}"
|
43
|
-
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def self.deserialize string_value, options = {}
|
48
|
-
|
49
|
-
left, right = float.to_s.split('.')
|
50
|
-
|
51
|
-
left = SortableIntegerAttribute.deserialize(left, options)
|
52
|
-
|
53
|
-
"#{left}.#{right}".to_f
|
54
|
-
|
55
|
-
end
|
56
|
-
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|
60
|
-
end
|
@@ -1,95 +0,0 @@
|
|
1
|
-
# Copyright 2011-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
4
|
-
# may not use this file except in compliance with the License. A copy of
|
5
|
-
# the License is located at
|
6
|
-
#
|
7
|
-
# http://aws.amazon.com/apache2.0/
|
8
|
-
#
|
9
|
-
# or in the "license" file accompanying this file. This file is
|
10
|
-
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
11
|
-
# ANY KIND, either express or implied. See the License for the specific
|
12
|
-
# language governing permissions and limitations under the License.
|
13
|
-
|
14
|
-
require 'aws/record/attributes/integer'
|
15
|
-
|
16
|
-
module AWS
|
17
|
-
module Record
|
18
|
-
|
19
|
-
# @private
|
20
|
-
class SortableIntegerAttribute < IntegerAttribute
|
21
|
-
|
22
|
-
def initialize name, options = {}
|
23
|
-
|
24
|
-
range = options[:range]
|
25
|
-
raise ArgumentError, "missing required option :range" unless range
|
26
|
-
raise ArgumentError, ":range should be a integer range" unless
|
27
|
-
range.is_a?(Range) and range.first.is_a?(Integer)
|
28
|
-
|
29
|
-
super(name, options)
|
30
|
-
|
31
|
-
end
|
32
|
-
|
33
|
-
# Returns a serialized representation of the integer value suitable for
|
34
|
-
# storing in SimpleDB.
|
35
|
-
#
|
36
|
-
# attribute.serialize(123)
|
37
|
-
# #=> '123'
|
38
|
-
#
|
39
|
-
# # padded to the correct number of digits
|
40
|
-
# attribute.serialize('123', :range => (0..10_000)
|
41
|
-
# #=> '00123'
|
42
|
-
#
|
43
|
-
# # offset applied to make all values positive
|
44
|
-
# attribute.serialize('-55', :range => (-100..10_000)
|
45
|
-
# #=> '00045'
|
46
|
-
#
|
47
|
-
# @param [Integer] integer The number to serialize.
|
48
|
-
# @param [Hash] options
|
49
|
-
# @option options [required,Range] :range A range that represents the
|
50
|
-
# minimum and maximum values this integer can be.
|
51
|
-
# The returned value will have an offset applied (if min is
|
52
|
-
# less than 0) and will be zero padded.
|
53
|
-
# @return [String] A serialized representation of the integer.
|
54
|
-
def self.serialize integer, options = {}
|
55
|
-
expect(Integer, integer) do
|
56
|
-
check_range(integer, options)
|
57
|
-
offset_and_precision(options) do |offset,precision|
|
58
|
-
"%0#{precision}d" % (integer.to_i + offset)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def self.deserialize string_value, options = {}
|
64
|
-
offset_and_precision(options) do |offset,precision|
|
65
|
-
string_value.to_i - offset
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
# @private
|
70
|
-
protected
|
71
|
-
def self.offset_and_precision options, &block
|
72
|
-
|
73
|
-
min = options[:range].first
|
74
|
-
max = options[:range].last
|
75
|
-
|
76
|
-
offset = min < 0 ? min * -1 : 0
|
77
|
-
precision = (max + offset).to_s.length
|
78
|
-
|
79
|
-
yield(offset, precision)
|
80
|
-
|
81
|
-
end
|
82
|
-
|
83
|
-
# @private
|
84
|
-
def self.check_range number, options
|
85
|
-
unless options[:range].include?(number)
|
86
|
-
msg = "unable to serialize `#{number}`, falls outside " +
|
87
|
-
"the range #{options[:range]}"
|
88
|
-
raise msg
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
end
|
93
|
-
|
94
|
-
end
|
95
|
-
end
|
@@ -1,69 +0,0 @@
|
|
1
|
-
# Copyright 2011-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
4
|
-
# may not use this file except in compliance with the License. A copy of
|
5
|
-
# the License is located at
|
6
|
-
#
|
7
|
-
# http://aws.amazon.com/apache2.0/
|
8
|
-
#
|
9
|
-
# or in the "license" file accompanying this file. This file is
|
10
|
-
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
11
|
-
# ANY KIND, either express or implied. See the License for the specific
|
12
|
-
# language governing permissions and limitations under the License.
|
13
|
-
|
14
|
-
require 'aws/record/attribute'
|
15
|
-
|
16
|
-
module AWS
|
17
|
-
module Record
|
18
|
-
|
19
|
-
# @private
|
20
|
-
class StringAttribute < Attribute
|
21
|
-
|
22
|
-
# Returns the value cast to a string. Empty strings are returned as
|
23
|
-
# nil by default. Type casting is done by calling #to_s on the value.
|
24
|
-
#
|
25
|
-
# string_attr.type_cast(123)
|
26
|
-
# # => '123'
|
27
|
-
#
|
28
|
-
# string_attr.type_cast('')
|
29
|
-
# # => nil
|
30
|
-
#
|
31
|
-
# string_attr.type_cast('', :preserve_empty_strings => true)
|
32
|
-
# # => ''
|
33
|
-
#
|
34
|
-
# @param [Mixed] value
|
35
|
-
# @param [Hash] options
|
36
|
-
# @option options [Boolean] :preserve_empty_strings (false) When true,
|
37
|
-
# empty strings are preserved and not cast to nil.
|
38
|
-
# @return [String,nil] The type casted value.
|
39
|
-
def self.type_cast raw_value, options = {}
|
40
|
-
case raw_value
|
41
|
-
when nil then nil
|
42
|
-
when '' then options[:preserve_empty_strings] ? '' : nil
|
43
|
-
when String then raw_value
|
44
|
-
else raw_value.to_s
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# Returns a serialized representation of the string value suitable for
|
49
|
-
# storing in SimpleDB.
|
50
|
-
# @param [String] string
|
51
|
-
# @param [Hash] options
|
52
|
-
# @return [String] The serialized string.
|
53
|
-
def self.serialize string, options = {}
|
54
|
-
unless string.is_a?(String)
|
55
|
-
msg = "expected a String value, got #{string.class}"
|
56
|
-
raise ArgumentError, msg
|
57
|
-
end
|
58
|
-
string
|
59
|
-
end
|
60
|
-
|
61
|
-
# @private
|
62
|
-
def self.allow_set?
|
63
|
-
true
|
64
|
-
end
|
65
|
-
|
66
|
-
end
|
67
|
-
|
68
|
-
end
|
69
|
-
end
|
data/lib/aws/record/base.rb
DELETED
@@ -1,828 +0,0 @@
|
|
1
|
-
# Copyright 2011-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
4
|
-
# may not use this file except in compliance with the License. A copy of
|
5
|
-
# the License is located at
|
6
|
-
#
|
7
|
-
# http://aws.amazon.com/apache2.0/
|
8
|
-
#
|
9
|
-
# or in the "license" file accompanying this file. This file is
|
10
|
-
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
11
|
-
# ANY KIND, either express or implied. See the License for the specific
|
12
|
-
# language governing permissions and limitations under the License.
|
13
|
-
|
14
|
-
# todo move these to included modules (like validations and naming)
|
15
|
-
|
16
|
-
require 'aws/simple_db'
|
17
|
-
|
18
|
-
require 'set'
|
19
|
-
require 'uuidtools'
|
20
|
-
|
21
|
-
require 'aws/record/naming'
|
22
|
-
require 'aws/record/attribute_macros'
|
23
|
-
require 'aws/record/scopes'
|
24
|
-
require 'aws/record/finder_methods'
|
25
|
-
require 'aws/record/optimistic_locking'
|
26
|
-
require 'aws/record/validations'
|
27
|
-
require 'aws/record/dirty_tracking'
|
28
|
-
require 'aws/record/conversion'
|
29
|
-
require 'aws/record/errors'
|
30
|
-
require 'aws/record/exceptions'
|
31
|
-
|
32
|
-
module AWS
|
33
|
-
module Record
|
34
|
-
|
35
|
-
# An ActiveRecord-like interface built ontop of AWS.
|
36
|
-
#
|
37
|
-
# class Book < AWS::Record::Base
|
38
|
-
#
|
39
|
-
# string_attr :title
|
40
|
-
# string_attr :author
|
41
|
-
# integer :number_of_pages
|
42
|
-
#
|
43
|
-
# timestamps # adds a :created_at and :updated_at pair of timestamps
|
44
|
-
#
|
45
|
-
# end
|
46
|
-
#
|
47
|
-
# b = Book.new(:title => 'My Book', :author => 'Me', :pages => 1)
|
48
|
-
# b.save
|
49
|
-
#
|
50
|
-
# = Attribute Macros
|
51
|
-
#
|
52
|
-
# When extending AWS::Record::Base you should first consider what
|
53
|
-
# attributes your class should have. Unlike ActiveRecord, AWS::Record
|
54
|
-
# models are not backed by a database table/schema. You must choose what
|
55
|
-
# attributes (and what types) you need.
|
56
|
-
#
|
57
|
-
# * +string_attr+
|
58
|
-
# * +boolean_attr+
|
59
|
-
# * +integer_attr+
|
60
|
-
# * +float_attr+
|
61
|
-
# * +datetime_attr+
|
62
|
-
#
|
63
|
-
# For more information about the various attribute macros available,
|
64
|
-
# and what options they accept, see {AttributeMacros}.
|
65
|
-
#
|
66
|
-
# === Usage
|
67
|
-
#
|
68
|
-
# Normally you just call these methods inside your model class definition:
|
69
|
-
#
|
70
|
-
# class Book < AWS::Record::Base
|
71
|
-
# string_attr :title
|
72
|
-
# boolean_attr :has_been_read
|
73
|
-
# integer_attr :number_of_pages
|
74
|
-
# float_attr :weight_in_pounds
|
75
|
-
# datetime_attr :published_at
|
76
|
-
# end
|
77
|
-
#
|
78
|
-
# For each attribute macro a pair of setter/getter methods are added #
|
79
|
-
# to your class (and a few other useful methods).
|
80
|
-
#
|
81
|
-
# b = Book.new
|
82
|
-
# b.title = "My Book"
|
83
|
-
# b.has_been_read = true
|
84
|
-
# b.number_of_pages = 1000
|
85
|
-
# b.weight_in_pounds = 1.1
|
86
|
-
# b.published_at = Time.now
|
87
|
-
# b.save
|
88
|
-
#
|
89
|
-
# b.id #=> "0aa894ca-8223-4d34-831e-e5134b2bb71c"
|
90
|
-
# b.attributes
|
91
|
-
# #=> { 'title' => 'My Book', 'has_been_read' => true, ... }
|
92
|
-
#
|
93
|
-
# === Default Values
|
94
|
-
#
|
95
|
-
# All attribute macros accept the +:default_value+ option. This sets
|
96
|
-
# a value that is populated onto all new instnaces of the class.
|
97
|
-
#
|
98
|
-
# class Book < AWS::Record::Base
|
99
|
-
# string_attr :author, :deafult_value => 'Me'
|
100
|
-
# end
|
101
|
-
#
|
102
|
-
# Book.new.author #=> 'Me'
|
103
|
-
#
|
104
|
-
# === Multi-Valued (Set) Attributes
|
105
|
-
#
|
106
|
-
# AWS::Record permits storing multiple values with a single attribute.
|
107
|
-
#
|
108
|
-
# class Book < AWS::Record::Base
|
109
|
-
# string_attr :tags, :set => true
|
110
|
-
# end
|
111
|
-
#
|
112
|
-
# b = Book.new
|
113
|
-
# b.tags #=> #<Set: {}>
|
114
|
-
#
|
115
|
-
# b.tags = ['fiction', 'fantasy']
|
116
|
-
# b.tags #=> #<Set: {'fiction', 'fantasy'}>
|
117
|
-
#
|
118
|
-
# These multi-valued attributes are treated as sets, not arrays. This
|
119
|
-
# means:
|
120
|
-
#
|
121
|
-
# * values are unordered
|
122
|
-
# * duplicate values are automatically omitted
|
123
|
-
#
|
124
|
-
# Please consider these limitations when you choose to use the +:set+
|
125
|
-
# option with the attribute macros.
|
126
|
-
#
|
127
|
-
# = Validations
|
128
|
-
#
|
129
|
-
# It's important to validate models before there are persisted to keep
|
130
|
-
# your data clean. AWS::Record supports most of the ActiveRecord style
|
131
|
-
# validators.
|
132
|
-
#
|
133
|
-
# class Book < AWS::Record::Base
|
134
|
-
# string_attr :title
|
135
|
-
# validates_presence_of :title
|
136
|
-
# end
|
137
|
-
#
|
138
|
-
# b = Book.new
|
139
|
-
# b.valid? #=> false
|
140
|
-
# b.errors.full_messages #=> ['Title may not be blank']
|
141
|
-
#
|
142
|
-
# Validations are checked before saving a record. If any of the validators
|
143
|
-
# adds an error, the the save will fail.
|
144
|
-
#
|
145
|
-
# For more information about the available validation methods see
|
146
|
-
# {Validations}.
|
147
|
-
#
|
148
|
-
# = Finder Methods
|
149
|
-
#
|
150
|
-
# You can find records by their ID. Each record gets a UUID when it
|
151
|
-
# is saved for the first time. You can use this ID to fetch the record
|
152
|
-
# at a latter time:
|
153
|
-
#
|
154
|
-
# b = Book["0aa894ca-8223-4d34-831e-e5134b2bb71c"]
|
155
|
-
#
|
156
|
-
# b = Book.find("0aa894ca-8223-4d34-831e-e5134b2bb71c")
|
157
|
-
#
|
158
|
-
# If you try to find a record by ID that has no data an error will
|
159
|
-
# be raised.
|
160
|
-
#
|
161
|
-
# === All
|
162
|
-
#
|
163
|
-
# You can enumerate all of your records using +all+.
|
164
|
-
#
|
165
|
-
# Book.all.each do |book|
|
166
|
-
# puts book.id
|
167
|
-
# end
|
168
|
-
#
|
169
|
-
# Book.find(:all) do |book|
|
170
|
-
# puts book.id
|
171
|
-
# end
|
172
|
-
#
|
173
|
-
# Be careful when enumerating all. Depending on the number of records
|
174
|
-
# and number of attributes each record has, this can take a while,
|
175
|
-
# causing quite a few requests.
|
176
|
-
#
|
177
|
-
# === First
|
178
|
-
#
|
179
|
-
# If you only want a single record, you should use +first+.
|
180
|
-
#
|
181
|
-
# b = Book.first
|
182
|
-
#
|
183
|
-
# === Modifiers
|
184
|
-
#
|
185
|
-
# Frequently you do not want ALL records or the very first record. You
|
186
|
-
# can pass options to +find+, +all+ and +first+.
|
187
|
-
#
|
188
|
-
# my_books = Book.find(:all, :where => 'owner = "Me"')
|
189
|
-
#
|
190
|
-
# book = Book.first(:where => { :has_been_read => false })
|
191
|
-
#
|
192
|
-
# You can pass as find options:
|
193
|
-
#
|
194
|
-
# * +:where+ - Conditions that must be met to be returned
|
195
|
-
# * +:order+ - The order to sort matched records by
|
196
|
-
# * +:limit+ - The maximum number of records to return
|
197
|
-
#
|
198
|
-
# = Scopes
|
199
|
-
#
|
200
|
-
# More useful than writing query fragments all over the place is to
|
201
|
-
# name your most common conditions for reuse.
|
202
|
-
#
|
203
|
-
# class Book < AWS::Record::Base
|
204
|
-
#
|
205
|
-
# scope :mine, where(:owner => 'Me')
|
206
|
-
#
|
207
|
-
# scope :unread, where(:has_been_read => false)
|
208
|
-
#
|
209
|
-
# scope :by_popularity, order(:score, :desc)
|
210
|
-
#
|
211
|
-
# scope :top_10, by_popularity.limit(10)
|
212
|
-
#
|
213
|
-
# end
|
214
|
-
#
|
215
|
-
# # The following expression returns 10 books that belong
|
216
|
-
# # to me, that are unread sorted by popularity.
|
217
|
-
# next_good_reads = Book.mine.unread.top_10
|
218
|
-
#
|
219
|
-
# There are 3 standard scope methods:
|
220
|
-
#
|
221
|
-
# * +where+
|
222
|
-
# * +order+
|
223
|
-
# * +limit+
|
224
|
-
#
|
225
|
-
# === Conditions (where)
|
226
|
-
#
|
227
|
-
# Where accepts aruments in a number of forms:
|
228
|
-
#
|
229
|
-
# 1. As an sql-like fragment. If you need to escape values this form is
|
230
|
-
# not suggested.
|
231
|
-
#
|
232
|
-
# Book.where('title = "My Book"')
|
233
|
-
#
|
234
|
-
# 2. An sql-like fragment, with placeholders. This escapes quoted
|
235
|
-
# arguments properly to avoid injection.
|
236
|
-
#
|
237
|
-
# Book.where('title = ?', 'My Book')
|
238
|
-
#
|
239
|
-
# 3. A hash of key-value pairs. This is the simplest form, but also the
|
240
|
-
# least flexible. You can not use this form if you need more complex
|
241
|
-
# expressions that use or.
|
242
|
-
#
|
243
|
-
# Book.where(:title => 'My Book')
|
244
|
-
#
|
245
|
-
# === Order
|
246
|
-
#
|
247
|
-
# This orders the records as returned by AWS. Default ordering is ascending.
|
248
|
-
# Pass the value :desc as a second argument to sort in reverse ordering.
|
249
|
-
#
|
250
|
-
# Book.order(:title) # alphabetical ordering
|
251
|
-
# Book.order(:title, :desc) # reverse alphabetical ordering
|
252
|
-
#
|
253
|
-
# You may only order by a single attribute. If you call order twice in the
|
254
|
-
# chain, the last call gets presedence:
|
255
|
-
#
|
256
|
-
# Book.order(:title).order(:price)
|
257
|
-
#
|
258
|
-
# In this example the books will be ordered by :price and the order(:title)
|
259
|
-
# is lost.
|
260
|
-
#
|
261
|
-
# === Limit
|
262
|
-
#
|
263
|
-
# Just call +limit+ with an integer argument. This sets the maximum
|
264
|
-
# number of records to retrieve:
|
265
|
-
#
|
266
|
-
# Book.limit(2)
|
267
|
-
#
|
268
|
-
# === Delayed Execution
|
269
|
-
#
|
270
|
-
# It should be noted that all finds are lazy (except +first+). This
|
271
|
-
# means the value returned is not an array of records, rather a handle
|
272
|
-
# to a {Scope} object that will return records when you enumerate over them.
|
273
|
-
#
|
274
|
-
# This allows you to build an expression without making unecessary requests.
|
275
|
-
# In the following example no request is made until the call to
|
276
|
-
# each_with_index.
|
277
|
-
#
|
278
|
-
# all_books = Books.all
|
279
|
-
# ten_books = all_books.limit(10)
|
280
|
-
#
|
281
|
-
# ten_books.each_with_index do |book,n|
|
282
|
-
# puts "#{n + 1} : #{book.title}"
|
283
|
-
# end
|
284
|
-
#
|
285
|
-
class Base
|
286
|
-
|
287
|
-
# for rails 3+ active model compatability
|
288
|
-
extend Naming
|
289
|
-
include Naming
|
290
|
-
|
291
|
-
extend Validations
|
292
|
-
extend AttributeMacros
|
293
|
-
extend FinderMethods
|
294
|
-
extend OptimisticLocking
|
295
|
-
extend Scopes
|
296
|
-
|
297
|
-
include Conversion
|
298
|
-
include DirtyTracking
|
299
|
-
|
300
|
-
# Constructs a new record for this class/domain.
|
301
|
-
#
|
302
|
-
# @param [Hash] attributes A set of attribute values to seed this record
|
303
|
-
# with. The attributes are bulk assigned.
|
304
|
-
#
|
305
|
-
# @return [Base] Returns a new record that has not been persisted yet.
|
306
|
-
#
|
307
|
-
def initialize attributes = {}
|
308
|
-
|
309
|
-
opts = attributes.dup
|
310
|
-
|
311
|
-
@_data = {}
|
312
|
-
|
313
|
-
@_domain = attributes.delete(:domain)
|
314
|
-
@_domain ||= attributes.delete('domain')
|
315
|
-
@_domain = self.class.domain_name(@_domain)
|
316
|
-
|
317
|
-
assign_default_values
|
318
|
-
|
319
|
-
bulk_assign(attributes)
|
320
|
-
|
321
|
-
end
|
322
|
-
|
323
|
-
# The id for each record is auto-generated. The default strategy
|
324
|
-
# generates uuid strings.
|
325
|
-
# @return [String] Returns the id string (uuid) for this record. Retuns
|
326
|
-
# nil if this is a new record that has not been persisted yet.
|
327
|
-
def id
|
328
|
-
@_id
|
329
|
-
end
|
330
|
-
|
331
|
-
# @return [String] Returns the name of the SimpleDB domain this record
|
332
|
-
# is persisted to or will be persisted to.
|
333
|
-
def domain
|
334
|
-
@_domain
|
335
|
-
end
|
336
|
-
|
337
|
-
# @return [Hash] A hash with attribute names as hash keys (strings) and
|
338
|
-
# attribute values (of mixed types) as hash values.
|
339
|
-
def attributes
|
340
|
-
attributes = Core::IndifferentHash.new
|
341
|
-
attributes['id'] = id if persisted?
|
342
|
-
self.class.attributes.keys.inject(attributes) do |hash,attr_name|
|
343
|
-
hash[attr_name] = __send__(attr_name)
|
344
|
-
hash
|
345
|
-
end
|
346
|
-
end
|
347
|
-
|
348
|
-
# Acts like {#update} but does not call {#save}.
|
349
|
-
#
|
350
|
-
# record.attributes = { :name => 'abc', :age => 20 }
|
351
|
-
#
|
352
|
-
# @param [Hash] attributes A hash of attributes to set on this record
|
353
|
-
# without calling save.
|
354
|
-
#
|
355
|
-
# @return [Hash] Returns the attribute hash that was passed in.
|
356
|
-
#
|
357
|
-
def attributes= attributes
|
358
|
-
bulk_assign(attributes)
|
359
|
-
end
|
360
|
-
|
361
|
-
# Persistence indicates if the record has been saved previously or not.
|
362
|
-
#
|
363
|
-
# @example
|
364
|
-
# @recipe = Recipe.new(:name => 'Buttermilk Pancackes')
|
365
|
-
# @recipe.persisted? #=> false
|
366
|
-
# @recipe.save!
|
367
|
-
# @recipe.persisted? #=> true
|
368
|
-
#
|
369
|
-
# @return [Boolean] Returns true if this record has been persisted.
|
370
|
-
def persisted?
|
371
|
-
!!@_persisted
|
372
|
-
end
|
373
|
-
|
374
|
-
# @return [Boolean] Returns true if this record has not been persisted
|
375
|
-
# to SimpleDB.
|
376
|
-
def new_record?
|
377
|
-
!persisted?
|
378
|
-
end
|
379
|
-
|
380
|
-
# @return [Boolean] Returns true if this record has no validation errors.
|
381
|
-
def valid?
|
382
|
-
run_validations
|
383
|
-
errors.empty?
|
384
|
-
end
|
385
|
-
|
386
|
-
# Creates new records, updates existing records.
|
387
|
-
# @return [Boolean] Returns true if the record saved without errors,
|
388
|
-
# false otherwise.
|
389
|
-
def save
|
390
|
-
if valid?
|
391
|
-
persisted? ? update : create
|
392
|
-
clear_changes!
|
393
|
-
true
|
394
|
-
else
|
395
|
-
false
|
396
|
-
end
|
397
|
-
end
|
398
|
-
|
399
|
-
# Creates new records, updates exsting records. If there is a validation
|
400
|
-
# error then an exception is raised.
|
401
|
-
# @raise [InvalidRecordError] Raised when the record has validation
|
402
|
-
# errors and can not be saved.
|
403
|
-
# @return [true] Returns true after a successful save.
|
404
|
-
def save!
|
405
|
-
raise InvalidRecordError.new(self) unless save
|
406
|
-
true
|
407
|
-
end
|
408
|
-
|
409
|
-
# Bulk assigns the attributes and then saves the record.
|
410
|
-
# @param [Hash] attribute_hash A hash of attribute names (keys) and
|
411
|
-
# attribute values to assign to this record.
|
412
|
-
# @return (see #save)
|
413
|
-
def update_attributes attribute_hash
|
414
|
-
bulk_assign(attribute_hash)
|
415
|
-
save
|
416
|
-
end
|
417
|
-
|
418
|
-
# Bulk assigns the attributes and then saves the record. Raises
|
419
|
-
# an exception (AWS::Record::InvalidRecordError) if the record is not
|
420
|
-
# valid.
|
421
|
-
# @param (see #update_attributes)
|
422
|
-
# @return [true]
|
423
|
-
def update_attributes! attribute_hash
|
424
|
-
if update_attributes(attribute_hash)
|
425
|
-
true
|
426
|
-
else
|
427
|
-
raise InvalidRecordError.new(self)
|
428
|
-
end
|
429
|
-
end
|
430
|
-
|
431
|
-
# Deletes the record.
|
432
|
-
# @return (see #delete_item)
|
433
|
-
def delete
|
434
|
-
if persisted?
|
435
|
-
if deleted?
|
436
|
-
raise 'unable to delete, this object has already been deleted'
|
437
|
-
else
|
438
|
-
delete_item
|
439
|
-
end
|
440
|
-
else
|
441
|
-
raise 'unable to delete, this object has not been saved yet'
|
442
|
-
end
|
443
|
-
end
|
444
|
-
|
445
|
-
# @return [Boolean] Returns true if this instance object has been deleted.
|
446
|
-
def deleted?
|
447
|
-
persisted? ? !!@_deleted : false
|
448
|
-
end
|
449
|
-
|
450
|
-
class << self
|
451
|
-
|
452
|
-
# @return [Hash<String,Attribute>] Returns a hash of all of the
|
453
|
-
# configured attributes for this class.
|
454
|
-
def attributes
|
455
|
-
@attributes ||= {}
|
456
|
-
end
|
457
|
-
|
458
|
-
# Allows you to override the default domain name for this record.
|
459
|
-
# The defualt domain name is the class name.
|
460
|
-
# @param [String] The domain name that should be used for this class.
|
461
|
-
def set_domain_name name
|
462
|
-
@_domain_name = name
|
463
|
-
end
|
464
|
-
|
465
|
-
# Returns the domain name this record class persists data into.
|
466
|
-
# The default domain name is the class name with the optional
|
467
|
-
# domain_prefix).
|
468
|
-
# @param [String] name Defaults to the name of this class.
|
469
|
-
# @return [String] Returns the full prefixed domain name for this class.
|
470
|
-
def domain_name name = nil
|
471
|
-
|
472
|
-
name = @_domain_name if name.nil?
|
473
|
-
name = self.name if name.nil?
|
474
|
-
name = name.name if name.is_a?(SimpleDB::Domain)
|
475
|
-
|
476
|
-
"#{Record.domain_prefix}#{name}"
|
477
|
-
|
478
|
-
end
|
479
|
-
|
480
|
-
# Creates the SimpleDB domain that is configured for this class.
|
481
|
-
# @param [String] name Name of the domain to create. Defaults to
|
482
|
-
# the name of this class. The +name+ will be prefixed with
|
483
|
-
# domain_prefix if one is set.
|
484
|
-
#
|
485
|
-
# @return [AWS::SimpleDB::Domain]
|
486
|
-
#
|
487
|
-
def create_domain name = nil
|
488
|
-
sdb.domains.create(domain_name(name))
|
489
|
-
end
|
490
|
-
|
491
|
-
# @return [AWS::SimpleDB::Domain] Returns a reference to the domain
|
492
|
-
# this class will save data to.
|
493
|
-
# @private
|
494
|
-
def sdb_domain name = nil
|
495
|
-
sdb.domains[domain_name(name)]
|
496
|
-
end
|
497
|
-
|
498
|
-
protected
|
499
|
-
def sdb
|
500
|
-
AWS::SimpleDB.new
|
501
|
-
end
|
502
|
-
|
503
|
-
end
|
504
|
-
|
505
|
-
# If you define a custom setter, you use #[]= to set the value
|
506
|
-
# on the record.
|
507
|
-
#
|
508
|
-
# class Book < AWS::Record::Base
|
509
|
-
#
|
510
|
-
# string_attr :name
|
511
|
-
#
|
512
|
-
# # replace the default #author= method
|
513
|
-
# def author= name
|
514
|
-
# self['author'] = name.blank? ? 'Anonymous' : name
|
515
|
-
# end
|
516
|
-
#
|
517
|
-
# end
|
518
|
-
#
|
519
|
-
# @param [String,Symbol] The attribute name to set a value for
|
520
|
-
# @param attribute_value The value to assign.
|
521
|
-
protected
|
522
|
-
def []= attribute_name, new_value
|
523
|
-
self.class.attribute_for(attribute_name) do |attribute|
|
524
|
-
|
525
|
-
if_tracking_changes do
|
526
|
-
original_value = type_cast(attribute, attribute_was(attribute.name))
|
527
|
-
incoming_value = type_cast(attribute, new_value)
|
528
|
-
if original_value == incoming_value
|
529
|
-
clear_change!(attribute.name)
|
530
|
-
else
|
531
|
-
attribute_will_change!(attribute.name)
|
532
|
-
end
|
533
|
-
end
|
534
|
-
|
535
|
-
@_data[attribute.name] = new_value
|
536
|
-
|
537
|
-
end
|
538
|
-
end
|
539
|
-
|
540
|
-
# Returns the typecasted value for the named attribute.
|
541
|
-
#
|
542
|
-
# book = Book.new(:title => 'My Book')
|
543
|
-
# book['title'] #=> 'My Book'
|
544
|
-
# book.title #=> 'My Book'
|
545
|
-
#
|
546
|
-
# === Intended Use
|
547
|
-
#
|
548
|
-
# This method's primary use is for getting/setting the value for
|
549
|
-
# an attribute inside a custom method:
|
550
|
-
#
|
551
|
-
# class Book < AWS::Record::Base
|
552
|
-
#
|
553
|
-
# string_attr :title
|
554
|
-
#
|
555
|
-
# def title
|
556
|
-
# self['title'] ? self['title'].upcase : nil
|
557
|
-
# end
|
558
|
-
#
|
559
|
-
# end
|
560
|
-
#
|
561
|
-
# book = Book.new(:title => 'My Book')
|
562
|
-
# book.title #=> 'MY BOOK'
|
563
|
-
#
|
564
|
-
# @param [String,Symbol] attribute_name The name of the attribute to fetch
|
565
|
-
# a value for.
|
566
|
-
# @return The current type-casted value for the named attribute.
|
567
|
-
protected
|
568
|
-
def [] attribute_name
|
569
|
-
self.class.attribute_for(attribute_name) do |attribute|
|
570
|
-
type_cast(attribute, @_data[attribute.name])
|
571
|
-
end
|
572
|
-
end
|
573
|
-
|
574
|
-
# @return [SimpleDB::Item] Returns a reference to the item as stored in
|
575
|
-
# simple db.
|
576
|
-
# @private
|
577
|
-
private
|
578
|
-
def sdb_item
|
579
|
-
sdb_domain.items[id]
|
580
|
-
end
|
581
|
-
|
582
|
-
# @return [SimpleDB::Domain] Returns the domain this record is
|
583
|
-
# persisted to or will be persisted to.
|
584
|
-
private
|
585
|
-
def sdb_domain
|
586
|
-
self.class.sdb_domain(domain)
|
587
|
-
end
|
588
|
-
|
589
|
-
# @private
|
590
|
-
private
|
591
|
-
def assign_default_values
|
592
|
-
# populate default attribute values
|
593
|
-
ignore_changes do
|
594
|
-
self.class.attributes.values.each do |attribute|
|
595
|
-
begin
|
596
|
-
# copy default values down so methods like #gsub! don't
|
597
|
-
# modify the default values for other objects
|
598
|
-
@_data[attribute.name] = attribute.default_value.clone
|
599
|
-
rescue TypeError
|
600
|
-
@_data[attribute.name] = attribute.default_value
|
601
|
-
end
|
602
|
-
end
|
603
|
-
end
|
604
|
-
end
|
605
|
-
|
606
|
-
# @return [true]
|
607
|
-
# @private
|
608
|
-
private
|
609
|
-
def delete_item
|
610
|
-
options = {}
|
611
|
-
add_optimistic_lock_expectation(options)
|
612
|
-
sdb_item.delete(options)
|
613
|
-
@_deleted = true
|
614
|
-
end
|
615
|
-
|
616
|
-
# @private
|
617
|
-
private
|
618
|
-
def bulk_assign hash
|
619
|
-
hash.each_pair do |attribute_name, attribute_value|
|
620
|
-
__send__("#{attribute_name}=", attribute_value)
|
621
|
-
end
|
622
|
-
end
|
623
|
-
|
624
|
-
# @private
|
625
|
-
# @todo need to do something about partial hyrdation of attributes
|
626
|
-
private
|
627
|
-
def hydrate id, data
|
628
|
-
|
629
|
-
@_id = id
|
630
|
-
|
631
|
-
# New objects are populated with default values, but we don't
|
632
|
-
# want these values to hang around when hydrating persisted values
|
633
|
-
# (those values may have been blanked out before save).
|
634
|
-
self.class.attributes.values.each do |attribute|
|
635
|
-
@_data[attribute.name] = nil
|
636
|
-
end
|
637
|
-
|
638
|
-
ignore_changes do
|
639
|
-
bulk_assign(deserialize_item_data(data))
|
640
|
-
end
|
641
|
-
|
642
|
-
@_persisted = true
|
643
|
-
|
644
|
-
end
|
645
|
-
|
646
|
-
# This function accepts a hash of item data (as returned from
|
647
|
-
# AttributeCollection#to_h or ItemData#attributes) and returns only
|
648
|
-
# the key/value pairs that are configured attribues for this class.
|
649
|
-
# @private
|
650
|
-
private
|
651
|
-
def deserialize_item_data item_data
|
652
|
-
|
653
|
-
marked_for_deletion = item_data['_delete_'] || []
|
654
|
-
|
655
|
-
data = {}
|
656
|
-
item_data.each_pair do |attr_name,values|
|
657
|
-
|
658
|
-
attribute = self.class.attributes[attr_name]
|
659
|
-
|
660
|
-
next unless attribute
|
661
|
-
next if marked_for_deletion.include?(attr_name)
|
662
|
-
|
663
|
-
if attribute.set?
|
664
|
-
data[attr_name] = values.map{|v| attribute.deserialize(v) }
|
665
|
-
else
|
666
|
-
data[attr_name] = attribute.deserialize(values.first)
|
667
|
-
end
|
668
|
-
|
669
|
-
end
|
670
|
-
data
|
671
|
-
end
|
672
|
-
|
673
|
-
# @private
|
674
|
-
private
|
675
|
-
def create
|
676
|
-
|
677
|
-
populate_id
|
678
|
-
touch_timestamps('created_at', 'updated_at')
|
679
|
-
increment_optimistic_lock_value
|
680
|
-
|
681
|
-
to_add = serialize_attributes
|
682
|
-
|
683
|
-
add_optimistic_lock_expectation(to_add)
|
684
|
-
sdb_item.attributes.add(to_add)
|
685
|
-
|
686
|
-
@_persisted = true
|
687
|
-
|
688
|
-
end
|
689
|
-
|
690
|
-
# @private
|
691
|
-
private
|
692
|
-
def update
|
693
|
-
|
694
|
-
return unless changed?
|
695
|
-
|
696
|
-
touch_timestamps('updated_at')
|
697
|
-
increment_optimistic_lock_value
|
698
|
-
|
699
|
-
to_update = {}
|
700
|
-
to_delete = []
|
701
|
-
|
702
|
-
# serialized_attributes will raise error if the entire record is blank
|
703
|
-
attribute_values = serialize_attributes
|
704
|
-
|
705
|
-
changed.each do |attr_name|
|
706
|
-
if values = attribute_values[attr_name]
|
707
|
-
to_update[attr_name] = values
|
708
|
-
else
|
709
|
-
to_delete << attr_name
|
710
|
-
end
|
711
|
-
end
|
712
|
-
|
713
|
-
add_optimistic_lock_expectation(to_update)
|
714
|
-
|
715
|
-
if to_delete.empty?
|
716
|
-
sdb_item.attributes.replace(to_update)
|
717
|
-
else
|
718
|
-
sdb_item.attributes.replace(to_update.merge('_delete_' => to_delete))
|
719
|
-
sdb_item.attributes.delete(to_delete + ['_delete_'])
|
720
|
-
end
|
721
|
-
|
722
|
-
end
|
723
|
-
|
724
|
-
# @private
|
725
|
-
private
|
726
|
-
def serialize_attributes
|
727
|
-
|
728
|
-
hash = {}
|
729
|
-
self.class.attributes.each_pair do |attribute_name,attribute|
|
730
|
-
values = serialize(attribute, @_data[attribute_name])
|
731
|
-
unless values.empty?
|
732
|
-
hash[attribute_name] = values
|
733
|
-
end
|
734
|
-
end
|
735
|
-
|
736
|
-
# simple db does not support persisting items without attribute values
|
737
|
-
raise EmptyRecordError.new(self) if hash.empty?
|
738
|
-
|
739
|
-
hash
|
740
|
-
|
741
|
-
end
|
742
|
-
|
743
|
-
# @private
|
744
|
-
private
|
745
|
-
def increment_optimistic_lock_value
|
746
|
-
if_locks_optimistically do |lock_attr_name|
|
747
|
-
if value = self[lock_attr_name]
|
748
|
-
self[lock_attr_name] += 1
|
749
|
-
else
|
750
|
-
self[lock_attr_name] = 1
|
751
|
-
end
|
752
|
-
end
|
753
|
-
end
|
754
|
-
|
755
|
-
# @private
|
756
|
-
private
|
757
|
-
def add_optimistic_lock_expectation options
|
758
|
-
if_locks_optimistically do |lock_attr_name|
|
759
|
-
was = attribute_was(lock_attr_name)
|
760
|
-
if was
|
761
|
-
options[:if] = { lock_attr_name => was.to_s }
|
762
|
-
else
|
763
|
-
options[:unless] = lock_attr_name
|
764
|
-
end
|
765
|
-
end
|
766
|
-
end
|
767
|
-
|
768
|
-
private
|
769
|
-
def if_locks_optimistically &block
|
770
|
-
if opt_lock_attr = self.class.optimistic_locking_attr
|
771
|
-
yield(opt_lock_attr.name)
|
772
|
-
end
|
773
|
-
end
|
774
|
-
|
775
|
-
# @private
|
776
|
-
private
|
777
|
-
def populate_id
|
778
|
-
@_id = UUIDTools::UUID.random_create.to_s
|
779
|
-
end
|
780
|
-
|
781
|
-
# @private
|
782
|
-
private
|
783
|
-
def touch_timestamps *attributes
|
784
|
-
time = Time.now
|
785
|
-
attributes.each do |attr_name|
|
786
|
-
if self.class.attributes[attr_name] and !attribute_changed?(attr_name)
|
787
|
-
__send__("#{attr_name}=", time)
|
788
|
-
end
|
789
|
-
end
|
790
|
-
end
|
791
|
-
|
792
|
-
# @private
|
793
|
-
private
|
794
|
-
def type_cast attribute, raw
|
795
|
-
if attribute.set?
|
796
|
-
values = Record.as_array(raw).inject([]) do |values,value|
|
797
|
-
values << attribute.type_cast(value)
|
798
|
-
values
|
799
|
-
end
|
800
|
-
Set.new(values.compact)
|
801
|
-
else
|
802
|
-
attribute.type_cast(raw)
|
803
|
-
end
|
804
|
-
end
|
805
|
-
|
806
|
-
# @private
|
807
|
-
private
|
808
|
-
def serialize attribute, raw
|
809
|
-
type_casted = type_cast(attribute, raw)
|
810
|
-
Record.as_array(type_casted).inject([]) do |values, value|
|
811
|
-
values << attribute.serialize(value)
|
812
|
-
values
|
813
|
-
end
|
814
|
-
end
|
815
|
-
|
816
|
-
# @private
|
817
|
-
private
|
818
|
-
def self.attribute_for attribute_name, &block
|
819
|
-
unless attributes[attribute_name.to_s]
|
820
|
-
raise UndefinedAttributeError.new(attribute_name.to_s)
|
821
|
-
end
|
822
|
-
yield(attributes[attribute_name.to_s])
|
823
|
-
end
|
824
|
-
|
825
|
-
end
|
826
|
-
|
827
|
-
end
|
828
|
-
end
|