activerecord 4.1.0 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +776 -1330
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/association_relation.rb +4 -0
- data/lib/active_record/associations/alias_tracker.rb +14 -13
- data/lib/active_record/associations/association.rb +2 -2
- data/lib/active_record/associations/association_scope.rb +83 -43
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -6
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +66 -29
- data/lib/active_record/associations/collection_proxy.rb +22 -26
- data/lib/active_record/associations/has_many_association.rb +65 -18
- data/lib/active_record/associations/has_many_through_association.rb +55 -27
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +20 -12
- data/lib/active_record/associations/preloader/association.rb +34 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +49 -59
- data/lib/active_record/associations/singular_association.rb +25 -4
- data/lib/active_record/associations/through_association.rb +23 -14
- data/lib/active_record/associations.rb +171 -42
- data/lib/active_record/attribute.rb +149 -0
- data/lib/active_record/attribute_assignment.rb +18 -10
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +98 -44
- data/lib/active_record/attribute_methods/primary_key.rb +14 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +37 -147
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
- data/lib/active_record/attribute_methods/write.rb +14 -21
- data/lib/active_record/attribute_methods.rb +67 -94
- data/lib/active_record/attribute_set/builder.rb +86 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attributes.rb +139 -0
- data/lib/active_record/autosave_association.rb +45 -38
- data/lib/active_record/base.rb +10 -20
- data/lib/active_record/callbacks.rb +7 -7
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
- data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
- data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
- data/lib/active_record/connection_adapters/column.rb +28 -239
- data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
- data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -27
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -374
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +127 -38
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
- data/lib/active_record/connection_handling.rb +3 -3
- data/lib/active_record/core.rb +143 -32
- data/lib/active_record/counter_cache.rb +60 -7
- data/lib/active_record/enum.rb +10 -11
- data/lib/active_record/errors.rb +49 -27
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +56 -70
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +35 -17
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +52 -49
- data/lib/active_record/model_schema.rb +49 -57
- data/lib/active_record/nested_attributes.rb +7 -7
- data/lib/active_record/null_relation.rb +19 -5
- data/lib/active_record/persistence.rb +50 -31
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +14 -11
- data/lib/active_record/railties/databases.rake +56 -54
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +286 -102
- data/lib/active_record/relation/batches.rb +0 -1
- data/lib/active_record/relation/calculations.rb +39 -31
- data/lib/active_record/relation/delegation.rb +2 -2
- data/lib/active_record/relation/finder_methods.rb +80 -36
- data/lib/active_record/relation/merger.rb +25 -30
- data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +11 -10
- data/lib/active_record/relation/query_methods.rb +141 -55
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +69 -30
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +58 -26
- data/lib/active_record/schema_migration.rb +11 -0
- data/lib/active_record/scoping/default.rb +8 -7
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +19 -10
- data/lib/active_record/tasks/database_tasks.rb +73 -7
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +11 -9
- data/lib/active_record/transactions.rb +37 -21
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +30 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
- data/lib/active_record/type/integer.rb +55 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +56 -0
- data/lib/active_record/type/string.rb +36 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +6 -4
- data/lib/active_record/validations/uniqueness.rb +11 -17
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +3 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +4 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +65 -10
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -4,6 +4,12 @@ require 'active_support/core_ext/module/remove_method'
|
|
4
4
|
require 'active_record/errors'
|
5
5
|
|
6
6
|
module ActiveRecord
|
7
|
+
class AssociationNotFoundError < ConfigurationError #:nodoc:
|
8
|
+
def initialize(record, association_name)
|
9
|
+
super("Association named '#{association_name}' was not found on #{record.class.name}; perhaps you misspelled it?")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
7
13
|
class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
|
8
14
|
def initialize(reflection, associated_class = nil)
|
9
15
|
super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
|
@@ -40,11 +46,17 @@ module ActiveRecord
|
|
40
46
|
end
|
41
47
|
end
|
42
48
|
|
49
|
+
class HasOneAssociationPolymorphicThroughError < ActiveRecordError #:nodoc:
|
50
|
+
def initialize(owner_class_name, reflection)
|
51
|
+
super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
43
55
|
class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError #:nodoc:
|
44
56
|
def initialize(reflection)
|
45
57
|
through_reflection = reflection.through_reflection
|
46
58
|
source_reflection_names = reflection.source_reflection_names
|
47
|
-
source_associations = reflection.through_reflection.klass.
|
59
|
+
source_associations = reflection.through_reflection.klass._reflections.keys
|
48
60
|
super("Could not find the source association(s) #{source_reflection_names.collect{ |a| a.inspect }.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?")
|
49
61
|
end
|
50
62
|
end
|
@@ -145,7 +157,7 @@ module ActiveRecord
|
|
145
157
|
association = association_instance_get(name)
|
146
158
|
|
147
159
|
if association.nil?
|
148
|
-
reflection
|
160
|
+
raise AssociationNotFoundError.new(self, name) unless reflection = self.class._reflect_on_association(name)
|
149
161
|
association = reflection.association_class.new(self, reflection)
|
150
162
|
association_instance_set(name, association)
|
151
163
|
end
|
@@ -196,12 +208,13 @@ module ActiveRecord
|
|
196
208
|
# For instance, +attributes+ and +connection+ would be bad choices for association names.
|
197
209
|
#
|
198
210
|
# == Auto-generated methods
|
211
|
+
# See also Instance Public methods below for more details.
|
199
212
|
#
|
200
213
|
# === Singular associations (one-to-one)
|
201
214
|
# | | belongs_to |
|
202
215
|
# generated methods | belongs_to | :polymorphic | has_one
|
203
216
|
# ----------------------------------+------------+--------------+---------
|
204
|
-
# other
|
217
|
+
# other(force_reload=false) | X | X | X
|
205
218
|
# other=(other) | X | X | X
|
206
219
|
# build_other(attributes={}) | X | | X
|
207
220
|
# create_other(attributes={}) | X | | X
|
@@ -211,7 +224,7 @@ module ActiveRecord
|
|
211
224
|
# | | | has_many
|
212
225
|
# generated methods | habtm | has_many | :through
|
213
226
|
# ----------------------------------+-------+----------+----------
|
214
|
-
# others
|
227
|
+
# others(force_reload=false) | X | X | X
|
215
228
|
# others=(other,other,...) | X | X | X
|
216
229
|
# other_ids | X | X | X
|
217
230
|
# other_ids=(id,id,...) | X | X | X
|
@@ -413,6 +426,10 @@ module ActiveRecord
|
|
413
426
|
# has_many :birthday_events, ->(user) { where starts_on: user.birthday }, class_name: 'Event'
|
414
427
|
# end
|
415
428
|
#
|
429
|
+
# Note: Joining, eager loading and preloading of these associations is not fully possible.
|
430
|
+
# These operations happen before instance creation and the scope will be called with a +nil+ argument.
|
431
|
+
# This can lead to unexpected behavior and is deprecated.
|
432
|
+
#
|
416
433
|
# == Association callbacks
|
417
434
|
#
|
418
435
|
# Similar to the normal callbacks that hook into the life cycle of an Active Record object,
|
@@ -436,9 +453,11 @@ module ActiveRecord
|
|
436
453
|
#
|
437
454
|
# Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+.
|
438
455
|
#
|
439
|
-
#
|
440
|
-
# added to the collection.
|
441
|
-
#
|
456
|
+
# If any of the +before_add+ callbacks throw an exception, the object will not be
|
457
|
+
# added to the collection.
|
458
|
+
#
|
459
|
+
# Similarly, if any of the +before_remove+ callbacks throw an exception, the object
|
460
|
+
# will not be removed from the collection.
|
442
461
|
#
|
443
462
|
# == Association extensions
|
444
463
|
#
|
@@ -530,8 +549,8 @@ module ActiveRecord
|
|
530
549
|
# end
|
531
550
|
#
|
532
551
|
# @firm = Firm.first
|
533
|
-
# @firm.clients.
|
534
|
-
# @firm.invoices
|
552
|
+
# @firm.clients.flat_map { |c| c.invoices } # select all invoices for all clients of the firm
|
553
|
+
# @firm.invoices # selects all invoices by going through the Client join model
|
535
554
|
#
|
536
555
|
# Similarly you can go through a +has_one+ association on the join model:
|
537
556
|
#
|
@@ -636,7 +655,7 @@ module ActiveRecord
|
|
636
655
|
# belongs_to :commenter
|
637
656
|
# end
|
638
657
|
#
|
639
|
-
# When using nested association, you will not be able to modify the association because there
|
658
|
+
# When using a nested association, you will not be able to modify the association because there
|
640
659
|
# is not enough information to know what modification to make. For example, if you tried to
|
641
660
|
# add a <tt>Commenter</tt> in the example above, there would be no way to tell how to set up the
|
642
661
|
# intermediate <tt>Post</tt> and <tt>Comment</tt> objects.
|
@@ -706,9 +725,9 @@ module ActiveRecord
|
|
706
725
|
# == Eager loading of associations
|
707
726
|
#
|
708
727
|
# Eager loading is a way to find objects of a certain class and a number of named associations.
|
709
|
-
#
|
728
|
+
# It is one of the easiest ways to prevent the dreaded N+1 problem in which fetching 100
|
710
729
|
# posts that each need to display their author triggers 101 database queries. Through the
|
711
|
-
# use of eager loading, the
|
730
|
+
# use of eager loading, the number of queries will be reduced from 101 to 2.
|
712
731
|
#
|
713
732
|
# class Post < ActiveRecord::Base
|
714
733
|
# belongs_to :author
|
@@ -738,16 +757,16 @@ module ActiveRecord
|
|
738
757
|
# Post.includes(:author, :comments).each do |post|
|
739
758
|
#
|
740
759
|
# This will load all comments with a single query. This reduces the total number of queries
|
741
|
-
# to 3.
|
760
|
+
# to 3. In general, the number of queries will be 1 plus the number of associations
|
742
761
|
# named (except if some of the associations are polymorphic +belongs_to+ - see below).
|
743
762
|
#
|
744
763
|
# To include a deep hierarchy of associations, use a hash:
|
745
764
|
#
|
746
|
-
# Post.includes(:author, {comments: {author: :gravatar}}).each do |post|
|
765
|
+
# Post.includes(:author, { comments: { author: :gravatar } }).each do |post|
|
747
766
|
#
|
748
|
-
#
|
749
|
-
# You can mix and match
|
750
|
-
# associations you want to load.
|
767
|
+
# The above code will load all the comments and all of their associated
|
768
|
+
# authors and gravatars. You can mix and match any combination of symbols,
|
769
|
+
# arrays, and hashes to retrieve the associations you want to load.
|
751
770
|
#
|
752
771
|
# All of this power shouldn't fool you into thinking that you can pull out huge amounts
|
753
772
|
# of data with no performance penalty just because you've reduced the number of queries.
|
@@ -756,8 +775,8 @@ module ActiveRecord
|
|
756
775
|
# cut down on the number of queries in a situation as the one described above.
|
757
776
|
#
|
758
777
|
# Since only one table is loaded at a time, conditions or orders cannot reference tables
|
759
|
-
# other than the main one. If this is the case Active Record falls back to the previously
|
760
|
-
# used LEFT OUTER JOIN based strategy. For example
|
778
|
+
# other than the main one. If this is the case, Active Record falls back to the previously
|
779
|
+
# used LEFT OUTER JOIN based strategy. For example:
|
761
780
|
#
|
762
781
|
# Post.includes([:author, :comments]).where(['comments.approved = ?', true])
|
763
782
|
#
|
@@ -767,11 +786,16 @@ module ActiveRecord
|
|
767
786
|
# like this can have unintended consequences.
|
768
787
|
# In the above example posts with no approved comments are not returned at all, because
|
769
788
|
# the conditions apply to the SQL statement as a whole and not just to the association.
|
789
|
+
#
|
770
790
|
# You must disambiguate column references for this fallback to happen, for example
|
771
791
|
# <tt>order: "author.name DESC"</tt> will work but <tt>order: "name DESC"</tt> will not.
|
772
792
|
#
|
773
|
-
# If you
|
774
|
-
#
|
793
|
+
# If you want to load all posts (including posts with no approved comments) then write
|
794
|
+
# your own LEFT OUTER JOIN query using ON
|
795
|
+
#
|
796
|
+
# Post.joins("LEFT OUTER JOIN comments ON comments.post_id = posts.id AND comments.approved = '1'")
|
797
|
+
#
|
798
|
+
# In this case it is usually more natural to include an association which has conditions defined on it:
|
775
799
|
#
|
776
800
|
# class Post < ActiveRecord::Base
|
777
801
|
# has_many :approved_comments, -> { where approved: true }, class_name: 'Comment'
|
@@ -1036,6 +1060,9 @@ module ActiveRecord
|
|
1036
1060
|
# Specifies a one-to-many association. The following methods for retrieval and query of
|
1037
1061
|
# collections of associated objects will be added:
|
1038
1062
|
#
|
1063
|
+
# +collection+ is a placeholder for the symbol passed as the +name+ argument, so
|
1064
|
+
# <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.
|
1065
|
+
#
|
1039
1066
|
# [collection(force_reload = false)]
|
1040
1067
|
# Returns an array of all the associated objects.
|
1041
1068
|
# An empty array is returned if none are found.
|
@@ -1094,9 +1121,6 @@ module ActiveRecord
|
|
1094
1121
|
# Does the same as <tt>collection.create</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt>
|
1095
1122
|
# if the record is invalid.
|
1096
1123
|
#
|
1097
|
-
# (*Note*: +collection+ is replaced with the symbol passed as the first argument, so
|
1098
|
-
# <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.)
|
1099
|
-
#
|
1100
1124
|
# === Example
|
1101
1125
|
#
|
1102
1126
|
# A <tt>Firm</tt> class declares <tt>has_many :clients</tt>, which will add:
|
@@ -1115,7 +1139,32 @@ module ActiveRecord
|
|
1115
1139
|
# * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
|
1116
1140
|
# * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
|
1117
1141
|
# * <tt>Firm#clients.create!</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save!</tt>)
|
1118
|
-
# The declaration can also include an options hash to specialize the behavior of the association.
|
1142
|
+
# The declaration can also include an +options+ hash to specialize the behavior of the association.
|
1143
|
+
#
|
1144
|
+
# === Scopes
|
1145
|
+
#
|
1146
|
+
# You can pass a second argument +scope+ as a callable (i.e. proc or
|
1147
|
+
# lambda) to retrieve a specific set of records or customize the generated
|
1148
|
+
# query when you access the associated collection.
|
1149
|
+
#
|
1150
|
+
# Scope examples:
|
1151
|
+
# has_many :comments, -> { where(author_id: 1) }
|
1152
|
+
# has_many :employees, -> { joins(:address) }
|
1153
|
+
# has_many :posts, ->(post) { where("max_post_length > ?", post.length) }
|
1154
|
+
#
|
1155
|
+
# === Extensions
|
1156
|
+
#
|
1157
|
+
# The +extension+ argument allows you to pass a block into a has_many
|
1158
|
+
# association. This is useful for adding new finders, creators and other
|
1159
|
+
# factory-type methods to be used as part of the association.
|
1160
|
+
#
|
1161
|
+
# Extension examples:
|
1162
|
+
# has_many :employees do
|
1163
|
+
# def find_or_create_by_name(name)
|
1164
|
+
# first_name, last_name = name.split(" ", 2)
|
1165
|
+
# find_or_create_by(first_name: first_name, last_name: last_name)
|
1166
|
+
# end
|
1167
|
+
# end
|
1119
1168
|
#
|
1120
1169
|
# === Options
|
1121
1170
|
# [:class_name]
|
@@ -1127,8 +1176,14 @@ module ActiveRecord
|
|
1127
1176
|
# Specify the foreign key used for the association. By default this is guessed to be the name
|
1128
1177
|
# of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_many+
|
1129
1178
|
# association will use "person_id" as the default <tt>:foreign_key</tt>.
|
1179
|
+
# [:foreign_type]
|
1180
|
+
# Specify the column used to store the associated object's type, if this is a polymorphic
|
1181
|
+
# association. By default this is guessed to be the name of the polymorphic association
|
1182
|
+
# specified on "as" option with a "_type" suffix. So a class that defines a
|
1183
|
+
# <tt>has_many :tags, as: :taggable</tt> association will use "taggable_type" as the
|
1184
|
+
# default <tt>:foreign_type</tt>.
|
1130
1185
|
# [:primary_key]
|
1131
|
-
# Specify the
|
1186
|
+
# Specify the name of the column to use as the primary key for the association. By default this is +id+.
|
1132
1187
|
# [:dependent]
|
1133
1188
|
# Controls what happens to the associated objects when
|
1134
1189
|
# their owner is destroyed. Note that these are implemented as
|
@@ -1193,7 +1248,7 @@ module ActiveRecord
|
|
1193
1248
|
# Option examples:
|
1194
1249
|
# has_many :comments, -> { order "posted_on" }
|
1195
1250
|
# has_many :comments, -> { includes :author }
|
1196
|
-
# has_many :people, -> { where(
|
1251
|
+
# has_many :people, -> { where(deleted: false).order("name") }, class_name: "Person"
|
1197
1252
|
# has_many :tracks, -> { order "position" }, dependent: :destroy
|
1198
1253
|
# has_many :comments, dependent: :nullify
|
1199
1254
|
# has_many :tags, as: :taggable
|
@@ -1211,6 +1266,9 @@ module ActiveRecord
|
|
1211
1266
|
#
|
1212
1267
|
# The following methods for retrieval and query of a single associated object will be added:
|
1213
1268
|
#
|
1269
|
+
# +association+ is a placeholder for the symbol passed as the +name+ argument, so
|
1270
|
+
# <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.
|
1271
|
+
#
|
1214
1272
|
# [association(force_reload = false)]
|
1215
1273
|
# Returns the associated object. +nil+ is returned if none is found.
|
1216
1274
|
# [association=(associate)]
|
@@ -1229,9 +1287,6 @@ module ActiveRecord
|
|
1229
1287
|
# Does the same as <tt>create_association</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt>
|
1230
1288
|
# if the record is invalid.
|
1231
1289
|
#
|
1232
|
-
# (+association+ is replaced with the symbol passed as the first argument, so
|
1233
|
-
# <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.)
|
1234
|
-
#
|
1235
1290
|
# === Example
|
1236
1291
|
#
|
1237
1292
|
# An Account class declares <tt>has_one :beneficiary</tt>, which will add:
|
@@ -1241,9 +1296,20 @@ module ActiveRecord
|
|
1241
1296
|
# * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>)
|
1242
1297
|
# * <tt>Account#create_beneficiary!</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save!; b</tt>)
|
1243
1298
|
#
|
1299
|
+
# === Scopes
|
1300
|
+
#
|
1301
|
+
# You can pass a second argument +scope+ as a callable (i.e. proc or
|
1302
|
+
# lambda) to retrieve a specific record or customize the generated query
|
1303
|
+
# when you access the associated object.
|
1304
|
+
#
|
1305
|
+
# Scope examples:
|
1306
|
+
# has_one :author, -> { where(comment_id: 1) }
|
1307
|
+
# has_one :employer, -> { joins(:company) }
|
1308
|
+
# has_one :dob, ->(dob) { where("Date.new(2000, 01, 01) > ?", dob) }
|
1309
|
+
#
|
1244
1310
|
# === Options
|
1245
1311
|
#
|
1246
|
-
# The declaration can also include an options hash to specialize the behavior of the association.
|
1312
|
+
# The declaration can also include an +options+ hash to specialize the behavior of the association.
|
1247
1313
|
#
|
1248
1314
|
# Options are:
|
1249
1315
|
# [:class_name]
|
@@ -1263,6 +1329,12 @@ module ActiveRecord
|
|
1263
1329
|
# Specify the foreign key used for the association. By default this is guessed to be the name
|
1264
1330
|
# of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association
|
1265
1331
|
# will use "person_id" as the default <tt>:foreign_key</tt>.
|
1332
|
+
# [:foreign_type]
|
1333
|
+
# Specify the column used to store the associated object's type, if this is a polymorphic
|
1334
|
+
# association. By default this is guessed to be the name of the polymorphic association
|
1335
|
+
# specified on "as" option with a "_type" suffix. So a class that defines a
|
1336
|
+
# <tt>has_one :tag, as: :taggable</tt> association will use "taggable_type" as the
|
1337
|
+
# default <tt>:foreign_type</tt>.
|
1266
1338
|
# [:primary_key]
|
1267
1339
|
# Specify the method that returns the primary key used for the association. By default this is +id+.
|
1268
1340
|
# [:as]
|
@@ -1293,6 +1365,10 @@ module ActiveRecord
|
|
1293
1365
|
# that is the inverse of this <tt>has_one</tt> association. Does not work in combination
|
1294
1366
|
# with <tt>:through</tt> or <tt>:as</tt> options.
|
1295
1367
|
# See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
|
1368
|
+
# [:required]
|
1369
|
+
# When set to +true+, the association will also have its presence validated.
|
1370
|
+
# This will validate the association itself, not the id. You can use
|
1371
|
+
# +:inverse_of+ to avoid an extra query during validation.
|
1296
1372
|
#
|
1297
1373
|
# Option examples:
|
1298
1374
|
# has_one :credit_card, dependent: :destroy # destroys the associated credit card
|
@@ -1304,6 +1380,7 @@ module ActiveRecord
|
|
1304
1380
|
# has_one :boss, readonly: :true
|
1305
1381
|
# has_one :club, through: :membership
|
1306
1382
|
# has_one :primary_address, -> { where primary: true }, through: :addressables, source: :addressable
|
1383
|
+
# has_one :credit_card, required: true
|
1307
1384
|
def has_one(name, scope = nil, options = {})
|
1308
1385
|
reflection = Builder::HasOne.build(self, name, scope, options)
|
1309
1386
|
Reflection.add_reflection self, name, reflection
|
@@ -1317,6 +1394,9 @@ module ActiveRecord
|
|
1317
1394
|
# Methods will be added for retrieval and query for a single associated object, for which
|
1318
1395
|
# this object holds an id:
|
1319
1396
|
#
|
1397
|
+
# +association+ is a placeholder for the symbol passed as the +name+ argument, so
|
1398
|
+
# <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.
|
1399
|
+
#
|
1320
1400
|
# [association(force_reload = false)]
|
1321
1401
|
# Returns the associated object. +nil+ is returned if none is found.
|
1322
1402
|
# [association=(associate)]
|
@@ -1332,9 +1412,6 @@ module ActiveRecord
|
|
1332
1412
|
# Does the same as <tt>create_association</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt>
|
1333
1413
|
# if the record is invalid.
|
1334
1414
|
#
|
1335
|
-
# (+association+ is replaced with the symbol passed as the first argument, so
|
1336
|
-
# <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.)
|
1337
|
-
#
|
1338
1415
|
# === Example
|
1339
1416
|
#
|
1340
1417
|
# A Post class declares <tt>belongs_to :author</tt>, which will add:
|
@@ -1343,7 +1420,18 @@ module ActiveRecord
|
|
1343
1420
|
# * <tt>Post#build_author</tt> (similar to <tt>post.author = Author.new</tt>)
|
1344
1421
|
# * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
|
1345
1422
|
# * <tt>Post#create_author!</tt> (similar to <tt>post.author = Author.new; post.author.save!; post.author</tt>)
|
1346
|
-
# The declaration can also include an options hash to specialize the behavior of the association.
|
1423
|
+
# The declaration can also include an +options+ hash to specialize the behavior of the association.
|
1424
|
+
#
|
1425
|
+
# === Scopes
|
1426
|
+
#
|
1427
|
+
# You can pass a second argument +scope+ as a callable (i.e. proc or
|
1428
|
+
# lambda) to retrieve a specific record or customize the generated query
|
1429
|
+
# when you access the associated object.
|
1430
|
+
#
|
1431
|
+
# Scope examples:
|
1432
|
+
# belongs_to :user, -> { where(id: 2) }
|
1433
|
+
# belongs_to :user, -> { joins(:friends) }
|
1434
|
+
# belongs_to :level, ->(level) { where("game_level > ?", level.current) }
|
1347
1435
|
#
|
1348
1436
|
# === Options
|
1349
1437
|
#
|
@@ -1397,7 +1485,7 @@ module ActiveRecord
|
|
1397
1485
|
#
|
1398
1486
|
# Note that <tt>accepts_nested_attributes_for</tt> sets <tt>:autosave</tt> to <tt>true</tt>.
|
1399
1487
|
# [:touch]
|
1400
|
-
# If true, the associated object will be touched (the updated_at/on attributes set to
|
1488
|
+
# If true, the associated object will be touched (the updated_at/on attributes set to current time)
|
1401
1489
|
# when this record is either saved or destroyed. If you specify a symbol, that attribute
|
1402
1490
|
# will be updated with the current time in addition to the updated_at/on attribute.
|
1403
1491
|
# [:inverse_of]
|
@@ -1405,18 +1493,23 @@ module ActiveRecord
|
|
1405
1493
|
# object that is the inverse of this <tt>belongs_to</tt> association. Does not work in
|
1406
1494
|
# combination with the <tt>:polymorphic</tt> options.
|
1407
1495
|
# See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
|
1496
|
+
# [:required]
|
1497
|
+
# When set to +true+, the association will also have its presence validated.
|
1498
|
+
# This will validate the association itself, not the id. You can use
|
1499
|
+
# +:inverse_of+ to avoid an extra query during validation.
|
1408
1500
|
#
|
1409
1501
|
# Option examples:
|
1410
1502
|
# belongs_to :firm, foreign_key: "client_of"
|
1411
1503
|
# belongs_to :person, primary_key: "name", foreign_key: "person_name"
|
1412
1504
|
# belongs_to :author, class_name: "Person", foreign_key: "author_id"
|
1413
|
-
# belongs_to :valid_coupon, ->(o) { where "discounts >
|
1505
|
+
# belongs_to :valid_coupon, ->(o) { where "discounts > ?", o.payments_count },
|
1414
1506
|
# class_name: "Coupon", foreign_key: "coupon_id"
|
1415
1507
|
# belongs_to :attachable, polymorphic: true
|
1416
1508
|
# belongs_to :project, readonly: true
|
1417
1509
|
# belongs_to :post, counter_cache: true
|
1418
1510
|
# belongs_to :company, touch: true
|
1419
1511
|
# belongs_to :company, touch: :employees_last_updated_at
|
1512
|
+
# belongs_to :company, required: true
|
1420
1513
|
def belongs_to(name, scope = nil, options = {})
|
1421
1514
|
reflection = Builder::BelongsTo.build(self, name, scope, options)
|
1422
1515
|
Reflection.add_reflection self, name, reflection
|
@@ -1454,6 +1547,9 @@ module ActiveRecord
|
|
1454
1547
|
#
|
1455
1548
|
# Adds the following methods for retrieval and query:
|
1456
1549
|
#
|
1550
|
+
# +collection+ is a placeholder for the symbol passed as the +name+ argument, so
|
1551
|
+
# <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.
|
1552
|
+
#
|
1457
1553
|
# [collection(force_reload = false)]
|
1458
1554
|
# Returns an array of all the associated objects.
|
1459
1555
|
# An empty array is returned if none are found.
|
@@ -1495,9 +1591,6 @@ module ActiveRecord
|
|
1495
1591
|
# with +attributes+, linked to this object through the join table, and that has already been
|
1496
1592
|
# saved (if it passed the validation).
|
1497
1593
|
#
|
1498
|
-
# (+collection+ is replaced with the symbol passed as the first argument, so
|
1499
|
-
# <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.)
|
1500
|
-
#
|
1501
1594
|
# === Example
|
1502
1595
|
#
|
1503
1596
|
# A Developer class declares <tt>has_and_belongs_to_many :projects</tt>, which will add:
|
@@ -1515,7 +1608,34 @@ module ActiveRecord
|
|
1515
1608
|
# * <tt>Developer#projects.exists?(...)</tt>
|
1516
1609
|
# * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("developer_id" => id)</tt>)
|
1517
1610
|
# * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("developer_id" => id); c.save; c</tt>)
|
1518
|
-
# The declaration may include an options hash to specialize the behavior of the association.
|
1611
|
+
# The declaration may include an +options+ hash to specialize the behavior of the association.
|
1612
|
+
#
|
1613
|
+
# === Scopes
|
1614
|
+
#
|
1615
|
+
# You can pass a second argument +scope+ as a callable (i.e. proc or
|
1616
|
+
# lambda) to retrieve a specific set of records or customize the generated
|
1617
|
+
# query when you access the associated collection.
|
1618
|
+
#
|
1619
|
+
# Scope examples:
|
1620
|
+
# has_and_belongs_to_many :projects, -> { includes :milestones, :manager }
|
1621
|
+
# has_and_belongs_to_many :categories, ->(category) {
|
1622
|
+
# where("default_category = ?", category.name)
|
1623
|
+
# }
|
1624
|
+
#
|
1625
|
+
# === Extensions
|
1626
|
+
#
|
1627
|
+
# The +extension+ argument allows you to pass a block into a
|
1628
|
+
# has_and_belongs_to_many association. This is useful for adding new
|
1629
|
+
# finders, creators and other factory-type methods to be used as part of
|
1630
|
+
# the association.
|
1631
|
+
#
|
1632
|
+
# Extension examples:
|
1633
|
+
# has_and_belongs_to_many :contractors do
|
1634
|
+
# def find_or_create_by_name(name)
|
1635
|
+
# first_name, last_name = name.split(" ", 2)
|
1636
|
+
# find_or_create_by(first_name: first_name, last_name: last_name)
|
1637
|
+
# end
|
1638
|
+
# end
|
1519
1639
|
#
|
1520
1640
|
# === Options
|
1521
1641
|
#
|
@@ -1561,14 +1681,22 @@ module ActiveRecord
|
|
1561
1681
|
scope = nil
|
1562
1682
|
end
|
1563
1683
|
|
1684
|
+
habtm_reflection = ActiveRecord::Reflection::HasAndBelongsToManyReflection.new(name, scope, options, self)
|
1685
|
+
|
1564
1686
|
builder = Builder::HasAndBelongsToMany.new name, self, options
|
1565
1687
|
|
1566
1688
|
join_model = builder.through_model
|
1567
1689
|
|
1690
|
+
# FIXME: we should move this to the internal constants. Also people
|
1691
|
+
# should never directly access this constant so I'm not happy about
|
1692
|
+
# setting it.
|
1693
|
+
const_set join_model.name, join_model
|
1694
|
+
|
1568
1695
|
middle_reflection = builder.middle_reflection join_model
|
1569
1696
|
|
1570
1697
|
Builder::HasMany.define_callbacks self, middle_reflection
|
1571
1698
|
Reflection.add_reflection self, middle_reflection.name, middle_reflection
|
1699
|
+
middle_reflection.parent_reflection = [name.to_s, habtm_reflection]
|
1572
1700
|
|
1573
1701
|
include Module.new {
|
1574
1702
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
@@ -1584,11 +1712,12 @@ module ActiveRecord
|
|
1584
1712
|
hm_options[:through] = middle_reflection.name
|
1585
1713
|
hm_options[:source] = join_model.right_reflection.name
|
1586
1714
|
|
1587
|
-
[:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate].each do |k|
|
1715
|
+
[:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name].each do |k|
|
1588
1716
|
hm_options[k] = options[k] if options.key? k
|
1589
1717
|
end
|
1590
1718
|
|
1591
1719
|
has_many name, scope, hm_options, &extension
|
1720
|
+
self._reflections[name.to_s].parent_reflection = [name.to_s, habtm_reflection]
|
1592
1721
|
end
|
1593
1722
|
end
|
1594
1723
|
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Attribute # :nodoc:
|
3
|
+
class << self
|
4
|
+
def from_database(name, value, type)
|
5
|
+
FromDatabase.new(name, value, type)
|
6
|
+
end
|
7
|
+
|
8
|
+
def from_user(name, value, type)
|
9
|
+
FromUser.new(name, value, type)
|
10
|
+
end
|
11
|
+
|
12
|
+
def with_cast_value(name, value, type)
|
13
|
+
WithCastValue.new(name, value, type)
|
14
|
+
end
|
15
|
+
|
16
|
+
def null(name)
|
17
|
+
Null.new(name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def uninitialized(name, type)
|
21
|
+
Uninitialized.new(name, type)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :name, :value_before_type_cast, :type
|
26
|
+
|
27
|
+
# This method should not be called directly.
|
28
|
+
# Use #from_database or #from_user
|
29
|
+
def initialize(name, value_before_type_cast, type)
|
30
|
+
@name = name
|
31
|
+
@value_before_type_cast = value_before_type_cast
|
32
|
+
@type = type
|
33
|
+
end
|
34
|
+
|
35
|
+
def value
|
36
|
+
# `defined?` is cheaper than `||=` when we get back falsy values
|
37
|
+
@value = original_value unless defined?(@value)
|
38
|
+
@value
|
39
|
+
end
|
40
|
+
|
41
|
+
def original_value
|
42
|
+
type_cast(value_before_type_cast)
|
43
|
+
end
|
44
|
+
|
45
|
+
def value_for_database
|
46
|
+
type.type_cast_for_database(value)
|
47
|
+
end
|
48
|
+
|
49
|
+
def changed_from?(old_value)
|
50
|
+
type.changed?(old_value, value, value_before_type_cast)
|
51
|
+
end
|
52
|
+
|
53
|
+
def changed_in_place_from?(old_value)
|
54
|
+
type.changed_in_place?(old_value, value)
|
55
|
+
end
|
56
|
+
|
57
|
+
def with_value_from_user(value)
|
58
|
+
self.class.from_user(name, value, type)
|
59
|
+
end
|
60
|
+
|
61
|
+
def with_value_from_database(value)
|
62
|
+
self.class.from_database(name, value, type)
|
63
|
+
end
|
64
|
+
|
65
|
+
def with_cast_value(value)
|
66
|
+
self.class.with_cast_value(name, value, type)
|
67
|
+
end
|
68
|
+
|
69
|
+
def type_cast(*)
|
70
|
+
raise NotImplementedError
|
71
|
+
end
|
72
|
+
|
73
|
+
def initialized?
|
74
|
+
true
|
75
|
+
end
|
76
|
+
|
77
|
+
def ==(other)
|
78
|
+
self.class == other.class &&
|
79
|
+
name == other.name &&
|
80
|
+
value_before_type_cast == other.value_before_type_cast &&
|
81
|
+
type == other.type
|
82
|
+
end
|
83
|
+
|
84
|
+
protected
|
85
|
+
|
86
|
+
def initialize_dup(other)
|
87
|
+
if defined?(@value) && @value.duplicable?
|
88
|
+
@value = @value.dup
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class FromDatabase < Attribute # :nodoc:
|
93
|
+
def type_cast(value)
|
94
|
+
type.type_cast_from_database(value)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class FromUser < Attribute # :nodoc:
|
99
|
+
def type_cast(value)
|
100
|
+
type.type_cast_from_user(value)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class WithCastValue < Attribute # :nodoc:
|
105
|
+
def type_cast(value)
|
106
|
+
value
|
107
|
+
end
|
108
|
+
|
109
|
+
def changed_in_place_from?(old_value)
|
110
|
+
false
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class Null < Attribute # :nodoc:
|
115
|
+
def initialize(name)
|
116
|
+
super(name, nil, Type::Value.new)
|
117
|
+
end
|
118
|
+
|
119
|
+
def value
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
|
123
|
+
def with_value_from_database(value)
|
124
|
+
raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{name}`"
|
125
|
+
end
|
126
|
+
alias_method :with_value_from_user, :with_value_from_database
|
127
|
+
end
|
128
|
+
|
129
|
+
class Uninitialized < Attribute # :nodoc:
|
130
|
+
def initialize(name, type)
|
131
|
+
super(name, nil, type)
|
132
|
+
end
|
133
|
+
|
134
|
+
def value
|
135
|
+
if block_given?
|
136
|
+
yield name
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def value_for_database
|
141
|
+
end
|
142
|
+
|
143
|
+
def initialized?
|
144
|
+
false
|
145
|
+
end
|
146
|
+
end
|
147
|
+
private_constant :FromDatabase, :FromUser, :Null, :Uninitialized
|
148
|
+
end
|
149
|
+
end
|