adept_dynamoid 0.5.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Dynamoid.gemspec +193 -0
  4. data/Gemfile +23 -0
  5. data/Gemfile.lock +86 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.markdown +265 -0
  8. data/Rakefile +62 -0
  9. data/VERSION +1 -0
  10. data/doc/.nojekyll +0 -0
  11. data/doc/Dynamoid.html +312 -0
  12. data/doc/Dynamoid/Adapter.html +1385 -0
  13. data/doc/Dynamoid/Adapter/AwsSdk.html +1585 -0
  14. data/doc/Dynamoid/Adapter/Local.html +1574 -0
  15. data/doc/Dynamoid/Associations.html +131 -0
  16. data/doc/Dynamoid/Associations/Association.html +794 -0
  17. data/doc/Dynamoid/Associations/BelongsTo.html +158 -0
  18. data/doc/Dynamoid/Associations/ClassMethods.html +723 -0
  19. data/doc/Dynamoid/Associations/HasAndBelongsToMany.html +164 -0
  20. data/doc/Dynamoid/Associations/HasMany.html +164 -0
  21. data/doc/Dynamoid/Associations/HasOne.html +158 -0
  22. data/doc/Dynamoid/Associations/ManyAssociation.html +1640 -0
  23. data/doc/Dynamoid/Associations/SingleAssociation.html +598 -0
  24. data/doc/Dynamoid/Components.html +204 -0
  25. data/doc/Dynamoid/Config.html +395 -0
  26. data/doc/Dynamoid/Config/Options.html +609 -0
  27. data/doc/Dynamoid/Criteria.html +131 -0
  28. data/doc/Dynamoid/Criteria/Chain.html +1063 -0
  29. data/doc/Dynamoid/Criteria/ClassMethods.html +98 -0
  30. data/doc/Dynamoid/Document.html +666 -0
  31. data/doc/Dynamoid/Document/ClassMethods.html +937 -0
  32. data/doc/Dynamoid/Errors.html +118 -0
  33. data/doc/Dynamoid/Errors/DocumentNotValid.html +210 -0
  34. data/doc/Dynamoid/Errors/Error.html +130 -0
  35. data/doc/Dynamoid/Errors/InvalidField.html +133 -0
  36. data/doc/Dynamoid/Errors/MissingRangeKey.html +133 -0
  37. data/doc/Dynamoid/Fields.html +669 -0
  38. data/doc/Dynamoid/Fields/ClassMethods.html +309 -0
  39. data/doc/Dynamoid/Finders.html +128 -0
  40. data/doc/Dynamoid/Finders/ClassMethods.html +516 -0
  41. data/doc/Dynamoid/Indexes.html +308 -0
  42. data/doc/Dynamoid/Indexes/ClassMethods.html +353 -0
  43. data/doc/Dynamoid/Indexes/Index.html +1104 -0
  44. data/doc/Dynamoid/Persistence.html +651 -0
  45. data/doc/Dynamoid/Persistence/ClassMethods.html +670 -0
  46. data/doc/Dynamoid/Validations.html +399 -0
  47. data/doc/_index.html +461 -0
  48. data/doc/class_list.html +47 -0
  49. data/doc/css/common.css +1 -0
  50. data/doc/css/full_list.css +55 -0
  51. data/doc/css/style.css +322 -0
  52. data/doc/file.LICENSE.html +66 -0
  53. data/doc/file.README.html +312 -0
  54. data/doc/file_list.html +52 -0
  55. data/doc/frames.html +13 -0
  56. data/doc/index.html +312 -0
  57. data/doc/js/app.js +205 -0
  58. data/doc/js/full_list.js +173 -0
  59. data/doc/js/jquery.js +16 -0
  60. data/doc/method_list.html +1238 -0
  61. data/doc/top-level-namespace.html +105 -0
  62. data/lib/dynamoid.rb +47 -0
  63. data/lib/dynamoid/adapter.rb +177 -0
  64. data/lib/dynamoid/adapter/aws_sdk.rb +223 -0
  65. data/lib/dynamoid/associations.rb +106 -0
  66. data/lib/dynamoid/associations/association.rb +105 -0
  67. data/lib/dynamoid/associations/belongs_to.rb +44 -0
  68. data/lib/dynamoid/associations/has_and_belongs_to_many.rb +40 -0
  69. data/lib/dynamoid/associations/has_many.rb +39 -0
  70. data/lib/dynamoid/associations/has_one.rb +39 -0
  71. data/lib/dynamoid/associations/many_association.rb +191 -0
  72. data/lib/dynamoid/associations/single_association.rb +69 -0
  73. data/lib/dynamoid/components.rb +36 -0
  74. data/lib/dynamoid/config.rb +57 -0
  75. data/lib/dynamoid/config/options.rb +78 -0
  76. data/lib/dynamoid/criteria.rb +29 -0
  77. data/lib/dynamoid/criteria/chain.rb +243 -0
  78. data/lib/dynamoid/dirty.rb +41 -0
  79. data/lib/dynamoid/document.rb +184 -0
  80. data/lib/dynamoid/errors.rb +28 -0
  81. data/lib/dynamoid/fields.rb +130 -0
  82. data/lib/dynamoid/finders.rb +131 -0
  83. data/lib/dynamoid/identity_map.rb +96 -0
  84. data/lib/dynamoid/indexes.rb +69 -0
  85. data/lib/dynamoid/indexes/index.rb +103 -0
  86. data/lib/dynamoid/middleware/identity_map.rb +16 -0
  87. data/lib/dynamoid/persistence.rb +247 -0
  88. data/lib/dynamoid/validations.rb +36 -0
  89. data/spec/app/models/address.rb +10 -0
  90. data/spec/app/models/camel_case.rb +24 -0
  91. data/spec/app/models/magazine.rb +11 -0
  92. data/spec/app/models/message.rb +9 -0
  93. data/spec/app/models/sponsor.rb +8 -0
  94. data/spec/app/models/subscription.rb +12 -0
  95. data/spec/app/models/tweet.rb +12 -0
  96. data/spec/app/models/user.rb +26 -0
  97. data/spec/dynamoid/adapter/aws_sdk_spec.rb +186 -0
  98. data/spec/dynamoid/adapter_spec.rb +117 -0
  99. data/spec/dynamoid/associations/association_spec.rb +194 -0
  100. data/spec/dynamoid/associations/belongs_to_spec.rb +71 -0
  101. data/spec/dynamoid/associations/has_and_belongs_to_many_spec.rb +47 -0
  102. data/spec/dynamoid/associations/has_many_spec.rb +42 -0
  103. data/spec/dynamoid/associations/has_one_spec.rb +45 -0
  104. data/spec/dynamoid/associations_spec.rb +16 -0
  105. data/spec/dynamoid/config_spec.rb +27 -0
  106. data/spec/dynamoid/criteria/chain_spec.rb +140 -0
  107. data/spec/dynamoid/criteria_spec.rb +72 -0
  108. data/spec/dynamoid/dirty_spec.rb +49 -0
  109. data/spec/dynamoid/document_spec.rb +118 -0
  110. data/spec/dynamoid/fields_spec.rb +127 -0
  111. data/spec/dynamoid/finders_spec.rb +135 -0
  112. data/spec/dynamoid/identity_map_spec.rb +45 -0
  113. data/spec/dynamoid/indexes/index_spec.rb +104 -0
  114. data/spec/dynamoid/indexes_spec.rb +25 -0
  115. data/spec/dynamoid/persistence_spec.rb +176 -0
  116. data/spec/dynamoid/validations_spec.rb +36 -0
  117. data/spec/dynamoid_spec.rb +9 -0
  118. data/spec/spec_helper.rb +50 -0
  119. metadata +376 -0
@@ -0,0 +1,41 @@
1
+ module Dynamoid
2
+ module Dirty
3
+ extend ActiveSupport::Concern
4
+ include ActiveModel::Dirty
5
+
6
+ module ClassMethods
7
+ def from_database(*)
8
+ super.tap { |d| d.changed_attributes.clear }
9
+ end
10
+ end
11
+
12
+ def save(*)
13
+ clear_changes { super }
14
+ end
15
+
16
+ def reload
17
+ super.tap { clear_changes }
18
+ end
19
+
20
+ def clear_changes
21
+ previous = changes
22
+ (block_given? ? yield : true).tap do |result|
23
+ unless result == false #failed validation; nil is OK.
24
+ @previously_changed = previous
25
+ changed_attributes.clear
26
+ end
27
+ end
28
+ end
29
+
30
+ def write_attribute(name, value)
31
+ attribute_will_change!(name) unless self.read_attribute(name) == value
32
+ super
33
+ end
34
+
35
+ protected
36
+
37
+ def attribute_method?(attr)
38
+ super || self.class.attributes.has_key?(attr.to_sym)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,184 @@
1
+ # encoding: utf-8
2
+ module Dynamoid #:nodoc:
3
+
4
+ # This is the base module for all domain objects that need to be persisted to
5
+ # the database as documents.
6
+ module Document
7
+ extend ActiveSupport::Concern
8
+ include Dynamoid::Components
9
+
10
+ included do
11
+ class_attribute :options, :read_only_attributes
12
+ self.options = {}
13
+ self.read_only_attributes = []
14
+
15
+ Dynamoid::Config.included_models << self
16
+ end
17
+
18
+ module ClassMethods
19
+ # Set up table options, including naming it whatever you want, setting the id key, and manually overriding read and
20
+ # write capacity.
21
+ #
22
+ # @param [Hash] options options to pass for this table
23
+ # @option options [Symbol] :name the name for the table; this still gets namespaced
24
+ # @option options [Symbol] :id id column for the table
25
+ # @option options [Integer] :read_capacity set the read capacity for the table; does not work on existing tables
26
+ # @option options [Integer] :write_capacity set the write capacity for the table; does not work on existing tables
27
+ #
28
+ # @since 0.4.0
29
+ def table(options = {})
30
+ self.options = options
31
+ end
32
+
33
+ def attr_readonly(*read_only_attributes)
34
+ self.read_only_attributes.concat read_only_attributes.map(&:to_s)
35
+ end
36
+
37
+ # Returns the read_capacity for this table.
38
+ #
39
+ # @since 0.4.0
40
+ def read_capacity
41
+ options[:read_capacity] || Dynamoid::Config.read_capacity
42
+ end
43
+
44
+ # Returns the write_capacity for this table.
45
+ #
46
+ # @since 0.4.0
47
+ def write_capacity
48
+ options[:write_capacity] || Dynamoid::Config.write_capacity
49
+ end
50
+
51
+ # Returns the id field for this class.
52
+ #
53
+ # @since 0.4.0
54
+ def hash_key
55
+ options[:key] || :id
56
+ end
57
+
58
+ # Initialize a new object and immediately save it to the database.
59
+ #
60
+ # @param [Hash] attrs Attributes with which to create the object.
61
+ #
62
+ # @return [Dynamoid::Document] the saved document
63
+ #
64
+ # @since 0.2.0
65
+ def create(attrs = {})
66
+ new(attrs).tap(&:save)
67
+ end
68
+
69
+ # Initialize a new object and immediately save it to the database. Raise an exception if persistence failed.
70
+ #
71
+ # @param [Hash] attrs Attributes with which to create the object.
72
+ #
73
+ # @return [Dynamoid::Document] the saved document
74
+ #
75
+ # @since 0.2.0
76
+ def create!(attrs = {})
77
+ new(attrs).tap(&:save!)
78
+ end
79
+
80
+ # Initialize a new object.
81
+ #
82
+ # @param [Hash] attrs Attributes with which to create the object.
83
+ #
84
+ # @return [Dynamoid::Document] the new document
85
+ #
86
+ # @since 0.2.0
87
+ def build(attrs = {})
88
+ new(attrs)
89
+ end
90
+
91
+ # Does this object exist?
92
+ #
93
+ # @param [String] id the id of the object
94
+ #
95
+ # @return [Boolean] true/false
96
+ #
97
+ # @since 0.2.0
98
+ def exists?(id)
99
+ !! find(id)
100
+ end
101
+ end
102
+
103
+ # Initialize a new object.
104
+ #
105
+ # @param [Hash] attrs Attributes with which to create the object.
106
+ #
107
+ # @return [Dynamoid::Document] the new document
108
+ #
109
+ # @since 0.2.0
110
+ def initialize(attrs = {})
111
+ run_callbacks :initialize do
112
+ self.class.send(:field, self.class.hash_key) unless self.respond_to?(self.class.hash_key)
113
+
114
+ @new_record = true
115
+ @attributes ||= {}
116
+ @associations ||= {}
117
+
118
+ load(attrs)
119
+ end
120
+ end
121
+
122
+ def load(attrs)
123
+ self.class.undump(attrs).each {|key, value| send "#{key}=", value }
124
+ end
125
+
126
+ # An object is equal to another object if their ids are equal.
127
+ #
128
+ # @since 0.2.0
129
+ def ==(other)
130
+ if self.class.identity_map_on?
131
+ super
132
+ else
133
+ return false if other.nil?
134
+ other.respond_to?(:hash_key) && other.hash_key == self.hash_key
135
+ end
136
+ end
137
+
138
+ # Reload an object from the database -- if you suspect the object has changed in the datastore and you need those
139
+ # changes to be reflected immediately, you would call this method.
140
+ #
141
+ # @return [Dynamoid::Document] the document this method was called on
142
+ #
143
+ # @since 0.2.0
144
+ def reload
145
+ self.attributes = self.class.find(hash_key, :range_key => range_value).attributes
146
+ @associations.values.each(&:reset)
147
+ self
148
+ end
149
+
150
+ # Return an object's hash key, regardless of what it might be called to the object.
151
+ #
152
+ # @since 0.4.0
153
+ def hash_key
154
+ self.send(self.class.hash_key)
155
+ end
156
+
157
+ # Assign an object's hash key, regardless of what it might be called to the object.
158
+ #
159
+ # @since 0.4.0
160
+ def hash_key=(value)
161
+ self.send("#{self.class.hash_key}=", value)
162
+ end
163
+
164
+ def range_value
165
+ if range_key = self.class.range_key
166
+ self.send(range_key)
167
+ end
168
+ end
169
+
170
+ def range_value=(value)
171
+ self.send("#{self.class.range_key}=", value)
172
+ end
173
+
174
+ def range_value
175
+ if range_key = self.class.range_key
176
+ self.send(range_key)
177
+ end
178
+ end
179
+
180
+ def range_value=(value)
181
+ self.send("#{self.class.range_key}=", value)
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+ module Dynamoid
3
+
4
+ # All the error specific to Dynamoid.
5
+ module Errors
6
+
7
+ # Generic error class.
8
+ class Error < StandardError; end
9
+
10
+ # InvalidField is raised when an attribute is specified for an index, but the attribute does not exist.
11
+ class InvalidField < Error; end
12
+
13
+ # MissingRangeKey is raised when a table that requires a range key is quieried without one.
14
+ class MissingRangeKey < Error; end
15
+
16
+ # raised when the conditional check failed during update operation
17
+ class ConditionalCheckFailedException < Error; end
18
+
19
+ # DocumentNotValid is raised when the document fails validation.
20
+ class DocumentNotValid < Error
21
+ def initialize(document)
22
+ super("Validation failed: #{document.errors.full_messages.join(", ")}")
23
+ end
24
+ end
25
+
26
+ class InvalidQuery < Error; end
27
+ end
28
+ end
@@ -0,0 +1,130 @@
1
+ # encoding: utf-8
2
+ module Dynamoid #:nodoc:
3
+
4
+ # All fields on a Dynamoid::Document must be explicitly defined -- if you have fields in the database that are not
5
+ # specified with field, then they will be ignored.
6
+ module Fields
7
+ extend ActiveSupport::Concern
8
+
9
+ # Initialize the attributes we know the class has, in addition to our magic attributes: id, created_at, and updated_at.
10
+ included do
11
+ class_attribute :attributes
12
+ class_attribute :range_key
13
+
14
+ self.attributes = {}
15
+ field :created_at, :datetime
16
+ field :updated_at, :datetime
17
+ end
18
+
19
+ module ClassMethods
20
+
21
+ # Specify a field for a document. Its type determines how it is coerced when read in and out of the datastore:
22
+ # default is string, but you can also specify :integer, :float, :set, :array, :datetime, and :serialized.
23
+ #
24
+ # @param [Symbol] name the name of the field
25
+ # @param [Symbol] type the type of the field (one of :integer, :float, :set, :array, :datetime, or :serialized)
26
+ # @param [Hash] options any additional options for the field
27
+ #
28
+ # @since 0.2.0
29
+ def field(name, type = :string, options = {})
30
+ named = name.to_s
31
+ self.attributes[name] = {:type => type}.merge(options)
32
+
33
+ define_method(named) { read_attribute(named) }
34
+ define_method("#{named}?") { !read_attribute(named).nil? }
35
+ define_method("#{named}=") {|value| write_attribute(named, value) }
36
+ end
37
+
38
+ def range(name, type = :string)
39
+ field(name, type)
40
+ self.range_key = name
41
+ end
42
+ end
43
+
44
+ # You can access the attributes of an object directly on its attributes method, which is by default an empty hash.
45
+ attr_accessor :attributes
46
+ alias :raw_attributes :attributes
47
+
48
+ # Write an attribute on the object. Also marks the previous value as dirty.
49
+ #
50
+ # @param [Symbol] name the name of the field
51
+ # @param [Object] value the value to assign to that field
52
+ #
53
+ # @since 0.2.0
54
+ def write_attribute(name, value)
55
+ if (size = value.to_s.size) > MAX_ITEM_SIZE
56
+ Dynamoid.logger.warn "DynamoDB can't store items larger than #{MAX_ITEM_SIZE} and the #{name} field has a length of #{size}."
57
+ end
58
+
59
+ if association = @associations[name]
60
+ association.reset
61
+ end
62
+
63
+ attributes[name.to_sym] = value
64
+ end
65
+ alias :[]= :write_attribute
66
+
67
+ # Read an attribute from an object.
68
+ #
69
+ # @param [Symbol] name the name of the field
70
+ #
71
+ # @since 0.2.0
72
+ def read_attribute(name)
73
+ attributes[name.to_sym]
74
+ end
75
+ alias :[] :read_attribute
76
+
77
+ # Updates multiple attibutes at once, saving the object once the updates are complete.
78
+ #
79
+ # @param [Hash] attributes a hash of attributes to update
80
+ #
81
+ # @since 0.2.0
82
+ def update_attributes(attributes)
83
+ attributes.each {|attribute, value| self.write_attribute(attribute, value)}
84
+ if self.new_record # if never saved save.
85
+ save
86
+ else # update attributes if we have saved.
87
+ # next if self.read_only_attributes.include? attribute.to_s put this back in.
88
+ run_callbacks(:save) do
89
+ update! do |u|
90
+ attributes.each do |attribute, value|
91
+ u.set attribute => dump_field(
92
+ self.read_attribute(attribute),
93
+ self.class.attributes[attribute.to_sym]
94
+ )
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ # Update a single attribute, saving the object afterwards.
102
+ #
103
+ # @param [Symbol] attribute the attribute to update
104
+ # @param [Object] value the value to assign it
105
+ #
106
+ # @since 0.2.0
107
+ def update_attribute(attribute, value)
108
+ write_attribute(attribute, value)
109
+ save
110
+ end
111
+
112
+ private
113
+
114
+ # Automatically called during the created callback to set the created_at time.
115
+ #
116
+ # @since 0.2.0
117
+ def set_created_at
118
+ self.created_at = DateTime.now
119
+ end
120
+
121
+ # Automatically called during the save callback to set the updated_at time.
122
+ #
123
+ # @since 0.2.0
124
+ def set_updated_at
125
+ self.updated_at = DateTime.now
126
+ end
127
+
128
+ end
129
+
130
+ end
@@ -0,0 +1,131 @@
1
+ # encoding: utf-8
2
+ module Dynamoid
3
+
4
+ # This module defines the finder methods that hang off the document at the
5
+ # class level, like find, find_by_id, and the method_missing style finders.
6
+ module Finders
7
+ extend ActiveSupport::Concern
8
+
9
+ module ClassMethods
10
+
11
+ # Find one or many objects, specified by one id or an array of ids.
12
+ #
13
+ # @param [Array/String] *id an array of ids or one single id
14
+ #
15
+ # @return [Dynamoid::Document] one object or an array of objects, depending on whether the input was an array or not
16
+ #
17
+ # @since 0.2.0
18
+ def find(*ids)
19
+
20
+ options = if ids.last.is_a? Hash
21
+ ids.slice!(-1)
22
+ else
23
+ {}
24
+ end
25
+
26
+ ids = Array(ids.flatten.uniq)
27
+ if ids.count == 1
28
+ self.find_by_id(ids.first, options)
29
+ else
30
+ find_all(ids)
31
+ end
32
+ end
33
+
34
+ # Find all object by hash key or hash and range key
35
+ #
36
+ # @param [Array<ID>] ids
37
+ #
38
+ # @example
39
+ # find all the user with hash key
40
+ # User.find_all(['1', '2', '3'])
41
+ #
42
+ # find all the tweets using hash key and range key
43
+ # Tweet.find_all([['1', 'red'], ['1', 'green'])
44
+ def find_all(ids)
45
+ items = Dynamoid::Adapter.read(self.table_name, ids, options)
46
+ items[self.table_name].collect{|i| from_database(i) }
47
+ end
48
+
49
+ # Find one object directly by id.
50
+ #
51
+ # @param [String] id the id of the object to find
52
+ #
53
+ # @return [Dynamoid::Document] the found object, or nil if nothing was found
54
+ #
55
+ # @since 0.2.0
56
+ def find_by_id(id, options = {})
57
+ if item = Dynamoid::Adapter.read(self.table_name, id, options)
58
+ from_database(item)
59
+ else
60
+ nil
61
+ end
62
+ end
63
+
64
+ # Find one object directly by hash and range keys
65
+ #
66
+ # @param [String] hash_key of the object to find
67
+ # @param [String/Integer/Float] range_key of the object to find
68
+ #
69
+ def find_by_composite_key(hash_key, range_key, options = {})
70
+ find_by_id(hash_key, options.merge({:range_key => range_key}))
71
+ end
72
+
73
+ # Find all objects by hash and range keys.
74
+ #
75
+ # @example find all ChamberTypes whose level is greater than 1
76
+ # class ChamberType
77
+ # include Dynamoid::Document
78
+ # field :chamber_type, :string
79
+ # range :level, :integer
80
+ # table :key => :chamber_type
81
+ # end
82
+ # ChamberType.find_all_by_composite_key('DustVault', range_greater_than: 1)
83
+ #
84
+ # @param [String] hash_key of the objects to find
85
+ # @param [Hash] options the options for the range key
86
+ # @option options [Range] :range_value find the range key within this range
87
+ # @option options [Number] :range_greater_than find range keys greater than this
88
+ # @option options [Number] :range_less_than find range keys less than this
89
+ # @option options [Number] :range_gte find range keys greater than or equal to this
90
+ # @option options [Number] :range_lte find range keys less than or equal to this
91
+ #
92
+ # @return [Array] an array of all matching items
93
+ #
94
+ def find_all_by_composite_key(hash_key, options = {})
95
+ Dynamoid::Adapter.query(self.table_name, options.merge({hash_value: hash_key})).collect do |item|
96
+ from_database(item)
97
+ end
98
+ end
99
+
100
+ # Find using exciting method_missing finders attributes. Uses criteria chains under the hood to accomplish this neatness.
101
+ #
102
+ # @example find a user by a first name
103
+ # User.find_by_first_name('Josh')
104
+ #
105
+ # @example find all users by first and last name
106
+ # User.find_all_by_first_name_and_last_name('Josh', 'Symonds')
107
+ #
108
+ # @return [Dynamoid::Document/Array] the found object, or an array of found objects if all was somewhere in the method
109
+ #
110
+ # @since 0.2.0
111
+ def method_missing(method, *args)
112
+ if method =~ /find/
113
+ finder = method.to_s.split('_by_').first
114
+ attributes = method.to_s.split('_by_').last.split('_and_')
115
+
116
+ chain = Dynamoid::Criteria::Chain.new(self)
117
+ chain.query = Hash.new.tap {|h| attributes.each_with_index {|attr, index| h[attr.to_sym] = args[index]}}
118
+
119
+ if finder =~ /all/
120
+ return chain.all
121
+ else
122
+ return chain.first
123
+ end
124
+ else
125
+ super
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ end