activerecord 5.0.0.beta2 → 5.0.0.beta3
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 +83 -20
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -0
- data/lib/active_record/associations/collection_association.rb +12 -1
- data/lib/active_record/associations/collection_proxy.rb +14 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +13 -7
- data/lib/active_record/associations/preloader/association.rb +1 -1
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/attribute_assignment.rb +0 -8
- data/lib/active_record/attribute_methods.rb +0 -24
- data/lib/active_record/attribute_methods/read.rb +5 -17
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -2
- data/lib/active_record/attribute_methods/write.rb +0 -13
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -2
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +4 -6
- data/lib/active_record/connection_adapters/abstract_adapter.rb +1 -2
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +10 -16
- data/lib/active_record/connection_adapters/connection_specification.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +2 -2
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +3 -4
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +0 -1
- data/lib/active_record/core.rb +5 -0
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/migration/compatibility.rb +1 -1
- data/lib/active_record/nested_attributes.rb +14 -6
- data/lib/active_record/null_relation.rb +1 -1
- data/lib/active_record/querying.rb +3 -3
- data/lib/active_record/reflection.rb +53 -36
- data/lib/active_record/relation.rb +26 -18
- data/lib/active_record/relation/batches.rb +4 -4
- data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
- data/lib/active_record/relation/calculations.rb +2 -10
- data/lib/active_record/relation/delegation.rb +2 -1
- data/lib/active_record/relation/finder_methods.rb +55 -26
- data/lib/active_record/relation/predicate_builder.rb +3 -4
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +10 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +11 -7
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/sanitization.rb +1 -1
- data/lib/active_record/schema_dumper.rb +6 -4
- data/lib/active_record/scoping/named.rb +10 -0
- data/lib/active_record/statement_cache.rb +1 -1
- data/lib/active_record/table_metadata.rb +5 -1
- data/lib/active_record/tasks/database_tasks.rb +4 -0
- data/lib/active_record/validations.rb +1 -1
- data/lib/active_record/validations/absence.rb +0 -1
- data/lib/active_record/validations/length.rb +0 -12
- data/lib/active_record/validations/presence.rb +0 -1
- data/lib/active_record/validations/uniqueness.rb +7 -9
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -0
- data/lib/rails/generators/active_record/model/templates/application_record.rb +3 -0
- metadata +9 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8926929a6df7248ea130b2445de91ba4e4933fb2
|
4
|
+
data.tar.gz: e25e460b0ff4541594badc944391f6a90b2d4c9f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: baf2428151cb30e7a384a1a7a63fbb9da784c9a4fe23b8900ee9dc0ba7735baed85ca24b5e1dd2a75d1625e6b6e0b6378764e1694623cf292d66cc189f0fe373
|
7
|
+
data.tar.gz: 1069aa6eb655e0ee291af703e6d49785603662274d42cc632d95d9b39e37f2facc5d79aa4a25dd24103bd7268ba40a8c5f2af8cc1ece4e7c9d437ff584313439
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,85 @@
|
|
1
|
+
## Rails 5.0.0.beta3 (February 24, 2016) ##
|
2
|
+
|
3
|
+
* Ensure that mutations of the array returned from `ActiveRecord::Relation#to_a`
|
4
|
+
do not affect the original relation, by returning a duplicate array each time.
|
5
|
+
|
6
|
+
This brings the behavior in line with `CollectionProxy#to_a`, which was
|
7
|
+
already more careful.
|
8
|
+
|
9
|
+
*Matthew Draper*
|
10
|
+
|
11
|
+
* Fixed `where` for polymorphic associations when passed an array containing different types.
|
12
|
+
|
13
|
+
Fixes #17011.
|
14
|
+
|
15
|
+
Example:
|
16
|
+
|
17
|
+
PriceEstimate.where(estimate_of: [Treasure.find(1), Car.find(2)])
|
18
|
+
# => SELECT "price_estimates".* FROM "price_estimates"
|
19
|
+
WHERE (("price_estimates"."estimate_of_type" = 'Treasure' AND "price_estimates"."estimate_of_id" = 1)
|
20
|
+
OR ("price_estimates"."estimate_of_type" = 'Car' AND "price_estimates"."estimate_of_id" = 2))
|
21
|
+
|
22
|
+
*Philippe Huibonhoa*
|
23
|
+
|
24
|
+
* Fix a bug where using `t.foreign_key` twice with the same `to_table` within
|
25
|
+
the same table definition would only create one foreign key.
|
26
|
+
|
27
|
+
*George Millo*
|
28
|
+
|
29
|
+
* Fix a regression on has many association, where calling a child from parent in child's callback
|
30
|
+
results in same child records getting added repeatedly to target.
|
31
|
+
|
32
|
+
Fixes #13387.
|
33
|
+
|
34
|
+
*Bogdan Gusiev*, *Jon Hinson*
|
35
|
+
|
36
|
+
* Rework `ActiveRecord::Relation#last`.
|
37
|
+
|
38
|
+
1. Never perform additional SQL on loaded relation
|
39
|
+
2. Use SQL reverse order instead of loading relation if relation doesn't have limit
|
40
|
+
3. Deprecated relation loading when SQL order can not be automatically reversed
|
41
|
+
|
42
|
+
Topic.order("title").load.last(3)
|
43
|
+
# before: SELECT ...
|
44
|
+
# after: No SQL
|
45
|
+
|
46
|
+
Topic.order("title").last
|
47
|
+
# before: SELECT * FROM `topics`
|
48
|
+
# after: SELECT * FROM `topics` ORDER BY `topics`.`title` DESC LIMIT 1
|
49
|
+
|
50
|
+
Topic.order("coalesce(author, title)").last
|
51
|
+
# before: SELECT * FROM `topics`
|
52
|
+
# after: Deprecation Warning for irreversible order
|
53
|
+
|
54
|
+
*Bogdan Gusiev*
|
55
|
+
|
56
|
+
|
57
|
+
* Allow `joins` to be unscoped.
|
58
|
+
|
59
|
+
Fixes #13775.
|
60
|
+
|
61
|
+
*Takashi Kokubun*
|
62
|
+
|
63
|
+
* Add ActiveRecord `#second_to_last` and `#third_to_last` methods.
|
64
|
+
|
65
|
+
*Brian Christian*
|
66
|
+
|
67
|
+
* Added `numeric` helper into migrations.
|
68
|
+
|
69
|
+
Example:
|
70
|
+
|
71
|
+
create_table(:numeric_types) do |t|
|
72
|
+
t.numeric :numeric_type, precision: 10, scale: 2
|
73
|
+
end
|
74
|
+
|
75
|
+
*Mehmet Emin İNAÇ*
|
76
|
+
|
77
|
+
* Bumped the minimum supported version of PostgreSQL to >= 9.1.
|
78
|
+
Both PG 9.0 and 8.4 are past their end of life date:
|
79
|
+
http://www.postgresql.org/support/versioning/
|
80
|
+
|
81
|
+
*Remo Mueller*
|
82
|
+
|
1
83
|
## Rails 5.0.0.beta2 (February 01, 2016) ##
|
2
84
|
|
3
85
|
* `ActiveRecord::Relation#reverse_order` throws `ActiveRecord::IrreversibleOrderError`
|
@@ -596,7 +678,7 @@
|
|
596
678
|
|
597
679
|
*Ben Murphy*, *Matthew Draper*
|
598
680
|
|
599
|
-
* `bin/
|
681
|
+
* `bin/rails db:migrate` uses
|
600
682
|
`ActiveRecord::Tasks::DatabaseTasks.migrations_paths` instead of
|
601
683
|
`Migrator.migrations_paths`.
|
602
684
|
|
@@ -998,13 +1080,6 @@
|
|
998
1080
|
|
999
1081
|
*Alex Coomans*
|
1000
1082
|
|
1001
|
-
* Dump indexes in `create_table` instead of `add_index`.
|
1002
|
-
|
1003
|
-
If the adapter supports indexes in `create_table`, generated SQL is
|
1004
|
-
slightly more efficient.
|
1005
|
-
|
1006
|
-
*Ryuta Kamizono*
|
1007
|
-
|
1008
1083
|
* Correctly dump `:options` on `create_table` for MySQL.
|
1009
1084
|
|
1010
1085
|
*Ryuta Kamizono*
|
@@ -1379,18 +1454,6 @@
|
|
1379
1454
|
|
1380
1455
|
*Chris Sinjakli*
|
1381
1456
|
|
1382
|
-
* Validation errors would be raised for parent records when an association
|
1383
|
-
was saved when the parent had `validate: false`. It should not be the
|
1384
|
-
responsibility of the model to validate an associated object unless the
|
1385
|
-
object was created or modified by the parent.
|
1386
|
-
|
1387
|
-
This fixes the issue by skipping validations if the parent record is
|
1388
|
-
persisted, not changed, and not marked for destruction.
|
1389
|
-
|
1390
|
-
Fixes #17621.
|
1391
|
-
|
1392
|
-
*Eileen M. Uchitelle*, *Aaron Patterson*
|
1393
|
-
|
1394
1457
|
* Fix n+1 query problem when eager loading nil associations (fixes #18312)
|
1395
1458
|
|
1396
1459
|
*Sammy Larbi*
|
@@ -72,7 +72,10 @@ module ActiveRecord
|
|
72
72
|
pk_type = reflection.primary_key_type
|
73
73
|
ids = Array(ids).reject(&:blank?)
|
74
74
|
ids.map! { |i| pk_type.cast(i) }
|
75
|
-
|
75
|
+
records = klass.where(reflection.association_primary_key => ids).index_by do |r|
|
76
|
+
r.send(reflection.association_primary_key)
|
77
|
+
end.values_at(*ids)
|
78
|
+
replace(records)
|
76
79
|
end
|
77
80
|
|
78
81
|
def reset
|
@@ -133,6 +136,14 @@ module ActiveRecord
|
|
133
136
|
first_nth_or_last(:forty_two, *args)
|
134
137
|
end
|
135
138
|
|
139
|
+
def third_to_last(*args)
|
140
|
+
first_nth_or_last(:third_to_last, *args)
|
141
|
+
end
|
142
|
+
|
143
|
+
def second_to_last(*args)
|
144
|
+
first_nth_or_last(:second_to_last, *args)
|
145
|
+
end
|
146
|
+
|
136
147
|
def last(*args)
|
137
148
|
first_nth_or_last(:last, *args)
|
138
149
|
end
|
@@ -197,6 +197,16 @@ module ActiveRecord
|
|
197
197
|
@association.forty_two(*args)
|
198
198
|
end
|
199
199
|
|
200
|
+
# Same as #first except returns only the third-to-last record.
|
201
|
+
def third_to_last(*args)
|
202
|
+
@association.third_to_last(*args)
|
203
|
+
end
|
204
|
+
|
205
|
+
# Same as #first except returns only the second-to-last record.
|
206
|
+
def second_to_last(*args)
|
207
|
+
@association.second_to_last(*args)
|
208
|
+
end
|
209
|
+
|
200
210
|
# Returns the last record, or the last +n+ records, from the collection.
|
201
211
|
# If the collection is empty, the first form returns +nil+, and the second
|
202
212
|
# form returns an empty array.
|
@@ -969,6 +979,10 @@ module ActiveRecord
|
|
969
979
|
end
|
970
980
|
alias_method :to_a, :to_ary
|
971
981
|
|
982
|
+
def records # :nodoc:
|
983
|
+
load_target
|
984
|
+
end
|
985
|
+
|
972
986
|
# Adds one or more +records+ to the collection by setting their foreign keys
|
973
987
|
# to the association's primary key. Returns +self+, so several appends may be
|
974
988
|
# chained together.
|
@@ -54,12 +54,18 @@ module ActiveRecord
|
|
54
54
|
end
|
55
55
|
scope_chain_index += 1
|
56
56
|
|
57
|
-
|
58
|
-
klass
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
57
|
+
klass_scope =
|
58
|
+
if klass.current_scope
|
59
|
+
klass.current_scope.clone
|
60
|
+
else
|
61
|
+
relation = ActiveRecord::Relation.create(
|
62
|
+
klass,
|
63
|
+
table,
|
64
|
+
predicate_builder,
|
65
|
+
)
|
66
|
+
klass.send(:build_default_scope, relation)
|
67
|
+
end
|
68
|
+
scope_chain_items.concat [klass_scope].compact
|
63
69
|
|
64
70
|
rel = scope_chain_items.inject(scope_chain_items.shift) do |left, right|
|
65
71
|
left.merge right
|
@@ -75,7 +81,7 @@ module ActiveRecord
|
|
75
81
|
column = klass.columns_hash[reflection.type.to_s]
|
76
82
|
|
77
83
|
binds << Relation::QueryAttribute.new(column.name, value, klass.type_for_attribute(column.name))
|
78
|
-
constraint = constraint.and
|
84
|
+
constraint = constraint.and klass.arel_attribute(reflection.type, table).eq(Arel::Nodes::BindParam.new)
|
79
85
|
end
|
80
86
|
|
81
87
|
joins << table.create_join(table, table.create_on(constraint), join_type)
|
@@ -47,7 +47,7 @@ module ActiveRecord
|
|
47
47
|
# This is overridden by HABTM as the condition should be on the foreign_key column in
|
48
48
|
# the join table
|
49
49
|
def association_key
|
50
|
-
table
|
50
|
+
klass.arel_attribute(association_key_name, table)
|
51
51
|
end
|
52
52
|
|
53
53
|
# The name of the key on the model which declares the association
|
@@ -29,14 +29,6 @@ module ActiveRecord
|
|
29
29
|
assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty?
|
30
30
|
end
|
31
31
|
|
32
|
-
# Tries to assign given value to given attribute.
|
33
|
-
# In case of an error, re-raises with the ActiveRecord constant.
|
34
|
-
def _assign_attribute(k, v) # :nodoc:
|
35
|
-
super
|
36
|
-
rescue ActiveModel::UnknownAttributeError
|
37
|
-
raise UnknownAttributeError.new(self, k)
|
38
|
-
end
|
39
|
-
|
40
32
|
# Assign any deferred nested attributes after the base attributes have been set.
|
41
33
|
def assign_nested_parameter_attributes(pairs)
|
42
34
|
pairs.each { |k, v| _assign_attribute(k, v) }
|
@@ -34,30 +34,6 @@ module ActiveRecord
|
|
34
34
|
|
35
35
|
BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
|
36
36
|
|
37
|
-
class AttributeMethodCache
|
38
|
-
def initialize
|
39
|
-
@module = Module.new
|
40
|
-
@method_cache = Concurrent::Map.new
|
41
|
-
end
|
42
|
-
|
43
|
-
def [](name)
|
44
|
-
@method_cache.compute_if_absent(name) do
|
45
|
-
safe_name = name.unpack('h*'.freeze).first
|
46
|
-
temp_method = "__temp__#{safe_name}"
|
47
|
-
ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
|
48
|
-
@module.module_eval method_body(temp_method, safe_name), __FILE__, __LINE__
|
49
|
-
@module.instance_method temp_method
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
# Override this method in the subclasses for method body.
|
56
|
-
def method_body(method_name, const_name)
|
57
|
-
raise NotImplementedError, "Subclasses must implement a method_body(method_name, const_name) method."
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
37
|
class GeneratedAttributeMethods < Module; end # :nodoc:
|
62
38
|
|
63
39
|
module ClassMethods
|
@@ -1,8 +1,11 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module AttributeMethods
|
3
3
|
module Read
|
4
|
-
|
5
|
-
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
protected
|
8
|
+
|
6
9
|
# We want to generate the methods via module_eval rather than
|
7
10
|
# define_method, because define_method is slower on dispatch.
|
8
11
|
# Evaluating many similar methods may use more memory as the instruction
|
@@ -21,21 +24,6 @@ module ActiveRecord
|
|
21
24
|
# to allocate an object on each call to the attribute method.
|
22
25
|
# Making it frozen means that it doesn't get duped when used to
|
23
26
|
# key the @attributes in read_attribute.
|
24
|
-
def method_body(method_name, const_name)
|
25
|
-
<<-EOMETHOD
|
26
|
-
def #{method_name}
|
27
|
-
name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
|
28
|
-
_read_attribute(name) { |n| missing_attribute(n, caller) }
|
29
|
-
end
|
30
|
-
EOMETHOD
|
31
|
-
end
|
32
|
-
}.new
|
33
|
-
|
34
|
-
extend ActiveSupport::Concern
|
35
|
-
|
36
|
-
module ClassMethods
|
37
|
-
protected
|
38
|
-
|
39
27
|
def define_method_attribute(name)
|
40
28
|
safe_name = name.unpack('h*'.freeze).first
|
41
29
|
temp_method = "__temp__#{safe_name}"
|
@@ -20,7 +20,7 @@ module ActiveRecord
|
|
20
20
|
nil
|
21
21
|
end
|
22
22
|
else
|
23
|
-
|
23
|
+
map_avoiding_infinite_recursion(super) { |v| cast(v) }
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -34,13 +34,23 @@ module ActiveRecord
|
|
34
34
|
elsif value.is_a?(::Float)
|
35
35
|
value
|
36
36
|
else
|
37
|
-
|
37
|
+
map_avoiding_infinite_recursion(value) { |v| convert_time_to_time_zone(v) }
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
41
|
def set_time_zone_without_conversion(value)
|
42
42
|
::Time.zone.local_to_utc(value).in_time_zone
|
43
43
|
end
|
44
|
+
|
45
|
+
def map_avoiding_infinite_recursion(value)
|
46
|
+
map(value) do |v|
|
47
|
+
if value.equal?(v)
|
48
|
+
nil
|
49
|
+
else
|
50
|
+
yield(v)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
44
54
|
end
|
45
55
|
|
46
56
|
extend ActiveSupport::Concern
|
@@ -1,19 +1,6 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module AttributeMethods
|
3
3
|
module Write
|
4
|
-
WriterMethodCache = Class.new(AttributeMethodCache) {
|
5
|
-
private
|
6
|
-
|
7
|
-
def method_body(method_name, const_name)
|
8
|
-
<<-EOMETHOD
|
9
|
-
def #{method_name}(value)
|
10
|
-
name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
|
11
|
-
write_attribute(name, value)
|
12
|
-
end
|
13
|
-
EOMETHOD
|
14
|
-
end
|
15
|
-
}.new
|
16
|
-
|
17
4
|
extend ActiveSupport::Concern
|
18
5
|
|
19
6
|
included do
|
data/lib/active_record/base.rb
CHANGED
@@ -13,7 +13,6 @@ require 'active_support/core_ext/kernel/singleton_class'
|
|
13
13
|
require 'active_support/core_ext/module/introspection'
|
14
14
|
require 'active_support/core_ext/object/duplicable'
|
15
15
|
require 'active_support/core_ext/class/subclasses'
|
16
|
-
require 'arel'
|
17
16
|
require 'active_record/attribute_decorators'
|
18
17
|
require 'active_record/errors'
|
19
18
|
require 'active_record/log_subscriber'
|
@@ -179,7 +179,7 @@ module ActiveRecord
|
|
179
179
|
#
|
180
180
|
# If the +before_validation+ callback throws +:abort+, the process will be
|
181
181
|
# aborted and {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] will return +false+.
|
182
|
-
# If {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] is called it will raise
|
182
|
+
# If {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] is called it will raise an ActiveRecord::RecordInvalid exception.
|
183
183
|
# Nothing will be appended to the errors object.
|
184
184
|
#
|
185
185
|
# == Canceling callbacks
|
@@ -27,10 +27,10 @@ module ActiveRecord
|
|
27
27
|
end
|
28
28
|
|
29
29
|
# Returns an ActiveRecord::Result instance.
|
30
|
-
def select_all(arel, name = nil, binds = [])
|
30
|
+
def select_all(arel, name = nil, binds = [], preparable: nil)
|
31
31
|
arel, binds = binds_from_relation arel, binds
|
32
32
|
sql = to_sql(arel, binds)
|
33
|
-
if arel.is_a?(String)
|
33
|
+
if !prepared_statements || (arel.is_a?(String) && preparable.nil?)
|
34
34
|
preparable = false
|
35
35
|
else
|
36
36
|
preparable = visitor.preparable
|
@@ -61,11 +61,11 @@ module ActiveRecord
|
|
61
61
|
@query_cache.clear
|
62
62
|
end
|
63
63
|
|
64
|
-
def select_all(arel, name = nil, binds = [])
|
64
|
+
def select_all(arel, name = nil, binds = [], preparable: nil)
|
65
65
|
if @query_cache_enabled && !locked?(arel)
|
66
66
|
arel, binds = binds_from_relation arel, binds
|
67
67
|
sql = to_sql(arel, binds)
|
68
|
-
cache_sql(sql, binds) { super(sql, name, binds) }
|
68
|
+
cache_sql(sql, binds) { super(sql, name, binds, preparable: visitor.preparable) }
|
69
69
|
else
|
70
70
|
super
|
71
71
|
end
|