aws-sdk 1.2.6 → 1.3.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.
- 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
|