activerecord 3.2.19 → 5.0.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 +7 -0
- data/CHANGELOG.md +1715 -604
- data/MIT-LICENSE +2 -2
- data/README.rdoc +40 -45
- data/examples/performance.rb +33 -22
- data/examples/simple.rb +3 -4
- data/lib/active_record/aggregations.rb +76 -51
- data/lib/active_record/association_relation.rb +35 -0
- data/lib/active_record/associations/alias_tracker.rb +54 -40
- data/lib/active_record/associations/association.rb +76 -56
- data/lib/active_record/associations/association_scope.rb +125 -93
- data/lib/active_record/associations/belongs_to_association.rb +57 -28
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
- data/lib/active_record/associations/builder/association.rb +120 -32
- data/lib/active_record/associations/builder/belongs_to.rb +115 -62
- data/lib/active_record/associations/builder/collection_association.rb +61 -53
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +117 -43
- data/lib/active_record/associations/builder/has_many.rb +9 -65
- data/lib/active_record/associations/builder/has_one.rb +18 -52
- data/lib/active_record/associations/builder/singular_association.rb +18 -19
- data/lib/active_record/associations/collection_association.rb +268 -186
- data/lib/active_record/associations/collection_proxy.rb +1003 -63
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +81 -41
- data/lib/active_record/associations/has_many_through_association.rb +76 -55
- data/lib/active_record/associations/has_one_association.rb +51 -21
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +83 -108
- data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
- data/lib/active_record/associations/join_dependency.rb +239 -155
- data/lib/active_record/associations/preloader/association.rb +97 -62
- data/lib/active_record/associations/preloader/collection_association.rb +2 -8
- data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +75 -33
- data/lib/active_record/associations/preloader.rb +111 -79
- data/lib/active_record/associations/singular_association.rb +35 -13
- data/lib/active_record/associations/through_association.rb +41 -19
- data/lib/active_record/associations.rb +727 -501
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +213 -0
- data/lib/active_record/attribute_assignment.rb +32 -162
- data/lib/active_record/attribute_decorators.rb +67 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
- data/lib/active_record/attribute_methods/dirty.rb +101 -61
- data/lib/active_record/attribute_methods/primary_key.rb +50 -36
- data/lib/active_record/attribute_methods/query.rb +7 -6
- data/lib/active_record/attribute_methods/read.rb +56 -117
- data/lib/active_record/attribute_methods/serialization.rb +43 -96
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +93 -42
- data/lib/active_record/attribute_methods/write.rb +34 -45
- data/lib/active_record/attribute_methods.rb +333 -144
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set/builder.rb +108 -0
- data/lib/active_record/attribute_set.rb +108 -0
- data/lib/active_record/attributes.rb +265 -0
- data/lib/active_record/autosave_association.rb +285 -223
- data/lib/active_record/base.rb +95 -490
- data/lib/active_record/callbacks.rb +95 -61
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/coders/yaml_column.rb +28 -19
- data/lib/active_record/collection_cache_key.rb +40 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +724 -277
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -192
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -26
- data/lib/active_record/connection_adapters/abstract/quoting.rb +140 -57
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +147 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +419 -276
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +105 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +963 -276
- data/lib/active_record/connection_adapters/abstract/transaction.rb +232 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +397 -106
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +643 -342
- data/lib/active_record/connection_adapters/column.rb +30 -259
- data/lib/active_record/connection_adapters/connection_specification.rb +263 -0
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +47 -196
- data/lib/active_record/connection_adapters/postgresql/column.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +170 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +70 -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 +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +48 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -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/json.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +93 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -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 +31 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +116 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +180 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +682 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -1039
- data/lib/active_record/connection_adapters/schema_cache.rb +74 -36
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +538 -24
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +155 -0
- data/lib/active_record/core.rb +561 -0
- data/lib/active_record/counter_cache.rb +146 -105
- data/lib/active_record/dynamic_matchers.rb +101 -64
- data/lib/active_record/enum.rb +234 -0
- data/lib/active_record/errors.rb +153 -56
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +10 -6
- data/lib/active_record/fixture_set/file.rb +77 -0
- data/lib/active_record/fixtures.rb +355 -232
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +144 -79
- data/lib/active_record/integration.rb +66 -13
- data/lib/active_record/internal_metadata.rb +56 -0
- data/lib/active_record/legacy_yaml_adapter.rb +46 -0
- data/lib/active_record/locale/en.yml +9 -1
- data/lib/active_record/locking/optimistic.rb +77 -56
- data/lib/active_record/locking/pessimistic.rb +6 -6
- data/lib/active_record/log_subscriber.rb +53 -28
- data/lib/active_record/migration/command_recorder.rb +166 -33
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +792 -264
- data/lib/active_record/model_schema.rb +192 -130
- data/lib/active_record/nested_attributes.rb +238 -145
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +89 -0
- data/lib/active_record/persistence.rb +357 -157
- data/lib/active_record/query_cache.rb +22 -43
- data/lib/active_record/querying.rb +34 -23
- data/lib/active_record/railtie.rb +88 -48
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +5 -4
- data/lib/active_record/railties/databases.rake +170 -422
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +2 -5
- data/lib/active_record/reflection.rb +715 -189
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/batches.rb +203 -50
- data/lib/active_record/relation/calculations.rb +203 -194
- data/lib/active_record/relation/delegation.rb +103 -25
- data/lib/active_record/relation/finder_methods.rb +457 -261
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +167 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +43 -0
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/predicate_builder.rb +153 -48
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +1019 -194
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +46 -150
- data/lib/active_record/relation/where_clause.rb +174 -0
- data/lib/active_record/relation/where_clause_factory.rb +38 -0
- data/lib/active_record/relation.rb +450 -245
- data/lib/active_record/result.rb +104 -12
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +120 -94
- data/lib/active_record/schema.rb +28 -18
- data/lib/active_record/schema_dumper.rb +141 -74
- data/lib/active_record/schema_migration.rb +50 -0
- data/lib/active_record/scoping/default.rb +64 -57
- data/lib/active_record/scoping/named.rb +93 -108
- data/lib/active_record/scoping.rb +73 -121
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +7 -5
- data/lib/active_record/statement_cache.rb +113 -0
- data/lib/active_record/store.rb +173 -15
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +68 -0
- data/lib/active_record/tasks/database_tasks.rb +313 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +151 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +110 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +59 -0
- data/lib/active_record/timestamp.rb +42 -24
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +233 -105
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +7 -0
- data/lib/active_record/type/date_time.rb +7 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/internal/abstract_json.rb +29 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +63 -0
- data/lib/active_record/type/time.rb +20 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type.rb +72 -0
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +33 -18
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +66 -0
- data/lib/active_record/validations/uniqueness.rb +128 -68
- data/lib/active_record/validations.rb +48 -40
- data/lib/active_record/version.rb +5 -7
- data/lib/active_record.rb +71 -47
- data/lib/rails/generators/active_record/migration/migration_generator.rb +56 -8
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +24 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +28 -16
- data/lib/rails/generators/active_record/migration.rb +18 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +38 -16
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +7 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -11
- metadata +188 -134
- data/examples/associations.png +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
- data/lib/active_record/associations/join_helper.rb +0 -55
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/serializers/xml_serializer.rb +0 -203
- data/lib/active_record/session_store.rb +0 -360
- data/lib/active_record/test_case.rb +0 -73
- data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
data/MIT-LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2004-
|
1
|
+
Copyright (c) 2004-2016 David Heinemeier Hansson
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
a copy of this software and associated documentation files (the
|
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
17
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
18
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
19
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= Active Record -- Object-relational mapping
|
1
|
+
= Active Record -- Object-relational mapping in Rails
|
2
2
|
|
3
3
|
Active Record connects classes to relational database tables to establish an
|
4
4
|
almost zero-configuration persistence layer for applications. The library
|
@@ -20,19 +20,19 @@ A short rundown of some of the major features:
|
|
20
20
|
class Product < ActiveRecord::Base
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
|
23
|
+
{Learn more}[link:classes/ActiveRecord/Base.html]
|
24
|
+
|
25
|
+
The Product class is automatically mapped to the table named "products",
|
26
|
+
which might look like this:
|
25
27
|
|
26
28
|
CREATE TABLE products (
|
27
|
-
id int
|
29
|
+
id int NOT NULL auto_increment,
|
28
30
|
name varchar(255),
|
29
31
|
PRIMARY KEY (id)
|
30
32
|
);
|
31
33
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
{Learn more}[link:classes/ActiveRecord/Base.html]
|
34
|
+
This would also define the following accessors: <tt>Product#name</tt> and
|
35
|
+
<tt>Product#name=(new_name)</tt>.
|
36
36
|
|
37
37
|
|
38
38
|
* Associations between objects defined by simple class methods.
|
@@ -49,10 +49,10 @@ A short rundown of some of the major features:
|
|
49
49
|
* Aggregations of value objects.
|
50
50
|
|
51
51
|
class Account < ActiveRecord::Base
|
52
|
-
composed_of :balance, :
|
53
|
-
:
|
52
|
+
composed_of :balance, class_name: 'Money',
|
53
|
+
mapping: %w(balance amount)
|
54
54
|
composed_of :address,
|
55
|
-
:
|
55
|
+
mapping: [%w(address_street street), %w(address_city city)]
|
56
56
|
end
|
57
57
|
|
58
58
|
{Learn more}[link:classes/ActiveRecord/Aggregations/ClassMethods.html]
|
@@ -61,10 +61,10 @@ A short rundown of some of the major features:
|
|
61
61
|
* Validation rules that can differ for new or existing objects.
|
62
62
|
|
63
63
|
class Account < ActiveRecord::Base
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
64
|
+
validates :subdomain, :name, :email_address, :password, presence: true
|
65
|
+
validates :subdomain, uniqueness: true
|
66
|
+
validates :terms_of_service, acceptance: true, on: :create
|
67
|
+
validates :password, :email_address, confirmation: true, on: :create
|
68
68
|
end
|
69
69
|
|
70
70
|
{Learn more}[link:classes/ActiveRecord/Validations.html]
|
@@ -80,17 +80,6 @@ A short rundown of some of the major features:
|
|
80
80
|
{Learn more}[link:classes/ActiveRecord/Callbacks.html]
|
81
81
|
|
82
82
|
|
83
|
-
* Observers that react to changes in a model.
|
84
|
-
|
85
|
-
class CommentObserver < ActiveRecord::Observer
|
86
|
-
def after_create(comment) # is called just after Comment#save
|
87
|
-
CommentMailer.new_comment_email("david@loudthinking.com", comment).deliver
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
{Learn more}[link:classes/ActiveRecord/Observer.html]
|
92
|
-
|
93
|
-
|
94
83
|
* Inheritance hierarchies.
|
95
84
|
|
96
85
|
class Company < ActiveRecord::Base; end
|
@@ -124,32 +113,32 @@ A short rundown of some of the major features:
|
|
124
113
|
* Database abstraction through simple adapters.
|
125
114
|
|
126
115
|
# connect to SQLite3
|
127
|
-
ActiveRecord::Base.establish_connection(:
|
116
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: 'dbfile.sqlite3')
|
128
117
|
|
129
118
|
# connect to MySQL with authentication
|
130
119
|
ActiveRecord::Base.establish_connection(
|
131
|
-
:
|
132
|
-
:
|
133
|
-
:
|
134
|
-
:
|
135
|
-
:
|
120
|
+
adapter: 'mysql2',
|
121
|
+
host: 'localhost',
|
122
|
+
username: 'me',
|
123
|
+
password: 'secret',
|
124
|
+
database: 'activerecord'
|
136
125
|
)
|
137
126
|
|
138
127
|
{Learn more}[link:classes/ActiveRecord/Base.html] and read about the built-in support for
|
139
|
-
MySQL[link:classes/ActiveRecord/ConnectionAdapters/
|
128
|
+
MySQL[link:classes/ActiveRecord/ConnectionAdapters/Mysql2Adapter.html],
|
140
129
|
PostgreSQL[link:classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], and
|
141
130
|
SQLite3[link:classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html].
|
142
131
|
|
143
132
|
|
144
|
-
* Logging support for Log4r[
|
133
|
+
* Logging support for Log4r[https://github.com/colbygk/log4r] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc].
|
145
134
|
|
146
|
-
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
147
|
-
ActiveRecord::Base.logger = Log4r::Logger.new(
|
135
|
+
ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
|
136
|
+
ActiveRecord::Base.logger = Log4r::Logger.new('Application Log')
|
148
137
|
|
149
138
|
|
150
139
|
* Database agnostic schema management with Migrations.
|
151
140
|
|
152
|
-
class AddSystemSettings < ActiveRecord::Migration
|
141
|
+
class AddSystemSettings < ActiveRecord::Migration[5.0]
|
153
142
|
def up
|
154
143
|
create_table :system_settings do |t|
|
155
144
|
t.string :name
|
@@ -159,7 +148,7 @@ A short rundown of some of the major features:
|
|
159
148
|
t.integer :position
|
160
149
|
end
|
161
150
|
|
162
|
-
SystemSetting.create :
|
151
|
+
SystemSetting.create name: 'notice', label: 'Use notice?', value: 1
|
163
152
|
end
|
164
153
|
|
165
154
|
def down
|
@@ -186,7 +175,7 @@ by relying on a number of conventions that make it easy for Active Record to inf
|
|
186
175
|
complex relations and structures from a minimal amount of explicit direction.
|
187
176
|
|
188
177
|
Convention over Configuration:
|
189
|
-
* No XML
|
178
|
+
* No XML files!
|
190
179
|
* Lots of reflection and run-time extension
|
191
180
|
* Magic is not inherently a bad word
|
192
181
|
|
@@ -199,24 +188,30 @@ Admit the Database:
|
|
199
188
|
|
200
189
|
The latest version of Active Record can be installed with RubyGems:
|
201
190
|
|
202
|
-
|
191
|
+
$ gem install activerecord
|
203
192
|
|
204
|
-
Source code can be downloaded as part of the Rails project on GitHub
|
193
|
+
Source code can be downloaded as part of the Rails project on GitHub:
|
205
194
|
|
206
|
-
* https://github.com/rails/rails/tree/
|
195
|
+
* https://github.com/rails/rails/tree/master/activerecord
|
207
196
|
|
208
197
|
|
209
198
|
== License
|
210
199
|
|
211
|
-
Active Record is released under the MIT license
|
200
|
+
Active Record is released under the MIT license:
|
201
|
+
|
202
|
+
* http://www.opensource.org/licenses/MIT
|
212
203
|
|
213
204
|
|
214
205
|
== Support
|
215
206
|
|
216
|
-
API documentation is at
|
207
|
+
API documentation is at:
|
217
208
|
|
218
209
|
* http://api.rubyonrails.org
|
219
210
|
|
220
|
-
Bug reports
|
211
|
+
Bug reports can be filed for the Ruby on Rails project here:
|
221
212
|
|
222
213
|
* https://github.com/rails/rails/issues
|
214
|
+
|
215
|
+
Feature requests should be discussed on the rails-core mailing list here:
|
216
|
+
|
217
|
+
* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
|
data/examples/performance.rb
CHANGED
@@ -1,16 +1,15 @@
|
|
1
|
-
require File.expand_path('../../../load_paths', __FILE__)
|
2
1
|
require "active_record"
|
3
2
|
require 'benchmark/ips'
|
4
3
|
|
5
4
|
TIME = (ENV['BENCHMARK_TIME'] || 20).to_i
|
6
5
|
RECORDS = (ENV['BENCHMARK_RECORDS'] || TIME*1000).to_i
|
7
6
|
|
8
|
-
conn = { :
|
7
|
+
conn = { adapter: 'sqlite3', database: ':memory:' }
|
9
8
|
|
10
9
|
ActiveRecord::Base.establish_connection(conn)
|
11
10
|
|
12
11
|
class User < ActiveRecord::Base
|
13
|
-
connection.create_table :users, :
|
12
|
+
connection.create_table :users, force: true do |t|
|
14
13
|
t.string :name, :email
|
15
14
|
t.timestamps
|
16
15
|
end
|
@@ -19,7 +18,7 @@ class User < ActiveRecord::Base
|
|
19
18
|
end
|
20
19
|
|
21
20
|
class Exhibit < ActiveRecord::Base
|
22
|
-
connection.create_table :exhibits, :
|
21
|
+
connection.create_table :exhibits, force: true do |t|
|
23
22
|
t.belongs_to :user
|
24
23
|
t.string :name
|
25
24
|
t.text :notes
|
@@ -39,10 +38,12 @@ class Exhibit < ActiveRecord::Base
|
|
39
38
|
where("notes IS NOT NULL")
|
40
39
|
end
|
41
40
|
|
42
|
-
def self.look(exhibits) exhibits.each
|
43
|
-
def self.feel(exhibits) exhibits.each
|
41
|
+
def self.look(exhibits) exhibits.each(&:look) end
|
42
|
+
def self.feel(exhibits) exhibits.each(&:feel) end
|
44
43
|
end
|
45
44
|
|
45
|
+
def progress_bar(int); print "." if (int%100).zero? ; end
|
46
|
+
|
46
47
|
puts 'Generating data...'
|
47
48
|
|
48
49
|
module ActiveRecord
|
@@ -75,30 +76,32 @@ notes = ActiveRecord::Faker::LOREM.join ' '
|
|
75
76
|
today = Date.today
|
76
77
|
|
77
78
|
puts "Inserting #{RECORDS} users and exhibits..."
|
78
|
-
RECORDS.times do
|
79
|
+
RECORDS.times do |record|
|
79
80
|
user = User.create(
|
80
|
-
:
|
81
|
-
:
|
82
|
-
:
|
81
|
+
created_at: today,
|
82
|
+
name: ActiveRecord::Faker.name,
|
83
|
+
email: ActiveRecord::Faker.email
|
83
84
|
)
|
84
85
|
|
85
86
|
Exhibit.create(
|
86
|
-
:
|
87
|
-
:
|
88
|
-
:
|
89
|
-
:
|
87
|
+
created_at: today,
|
88
|
+
name: ActiveRecord::Faker.name,
|
89
|
+
user: user,
|
90
|
+
notes: notes
|
90
91
|
)
|
92
|
+
progress_bar(record)
|
91
93
|
end
|
94
|
+
puts "Done!\n"
|
92
95
|
|
93
96
|
Benchmark.ips(TIME) do |x|
|
94
97
|
ar_obj = Exhibit.find(1)
|
95
|
-
attrs = { :
|
96
|
-
attrs_first = { :
|
97
|
-
attrs_second = { :
|
98
|
+
attrs = { name: 'sam' }
|
99
|
+
attrs_first = { name: 'sam' }
|
100
|
+
attrs_second = { name: 'tom' }
|
98
101
|
exhibit = {
|
99
|
-
:
|
100
|
-
:
|
101
|
-
:
|
102
|
+
name: ActiveRecord::Faker.name,
|
103
|
+
notes: notes,
|
104
|
+
created_at: Date.today
|
102
105
|
}
|
103
106
|
|
104
107
|
x.report("Model#id") do
|
@@ -117,10 +120,18 @@ Benchmark.ips(TIME) do |x|
|
|
117
120
|
Exhibit.first.look
|
118
121
|
end
|
119
122
|
|
123
|
+
x.report 'Model.take' do
|
124
|
+
Exhibit.take
|
125
|
+
end
|
126
|
+
|
120
127
|
x.report("Model.all limit(100)") do
|
121
128
|
Exhibit.look Exhibit.limit(100)
|
122
129
|
end
|
123
130
|
|
131
|
+
x.report("Model.all take(100)") do
|
132
|
+
Exhibit.look Exhibit.take(100)
|
133
|
+
end
|
134
|
+
|
124
135
|
x.report "Model.all limit(100) with relationship" do
|
125
136
|
Exhibit.feel Exhibit.limit(100).includes(:user)
|
126
137
|
end
|
@@ -143,7 +154,7 @@ Benchmark.ips(TIME) do |x|
|
|
143
154
|
end
|
144
155
|
|
145
156
|
x.report 'Resource#update' do
|
146
|
-
Exhibit.first.
|
157
|
+
Exhibit.first.update(name: 'bob')
|
147
158
|
end
|
148
159
|
|
149
160
|
x.report 'Resource#destroy' do
|
@@ -167,6 +178,6 @@ Benchmark.ips(TIME) do |x|
|
|
167
178
|
end
|
168
179
|
|
169
180
|
x.report "AR.execute(query)" do
|
170
|
-
ActiveRecord::Base.connection.execute("
|
181
|
+
ActiveRecord::Base.connection.execute("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}")
|
171
182
|
end
|
172
183
|
end
|
data/examples/simple.rb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
|
-
$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
|
2
1
|
require 'active_record'
|
3
2
|
|
4
3
|
class Person < ActiveRecord::Base
|
5
|
-
establish_connection :
|
6
|
-
connection.create_table table_name, :
|
4
|
+
establish_connection adapter: 'sqlite3', database: 'foobar.db'
|
5
|
+
connection.create_table table_name, force: true do |t|
|
7
6
|
t.string :name
|
8
7
|
end
|
9
8
|
end
|
10
9
|
|
11
|
-
bob = Person.create!(:
|
10
|
+
bob = Person.create!(name: 'bob')
|
12
11
|
puts Person.all.inspect
|
13
12
|
bob.destroy
|
14
13
|
puts Person.all.inspect
|
@@ -1,23 +1,40 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
#
|
3
|
-
module Aggregations
|
2
|
+
# See ActiveRecord::Aggregations::ClassMethods for documentation
|
3
|
+
module Aggregations
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
|
-
def
|
7
|
-
@aggregation_cache
|
6
|
+
def initialize_dup(*) # :nodoc:
|
7
|
+
@aggregation_cache = {}
|
8
|
+
super
|
8
9
|
end
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
def reload(*) # :nodoc:
|
12
|
+
clear_aggregation_cache
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def clear_aggregation_cache # :nodoc:
|
19
|
+
@aggregation_cache.clear if persisted?
|
20
|
+
end
|
21
|
+
|
22
|
+
def init_internals # :nodoc:
|
23
|
+
@aggregation_cache = {}
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
# Active Record implements aggregation through a macro-like class method called #composed_of
|
28
|
+
# for representing attributes as value objects. It expresses relationships like "Account [is]
|
12
29
|
# composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
|
13
|
-
# to the macro adds a description of how the value objects
|
14
|
-
# the entity object (when the entity is initialized either
|
15
|
-
# existing object) and how it can be turned back into attributes
|
30
|
+
# to the macro adds a description of how the value objects are created from the attributes of
|
31
|
+
# the entity object (when the entity is initialized either as a new object or from finding an
|
32
|
+
# existing object) and how it can be turned back into attributes (when the entity is saved to
|
16
33
|
# the database).
|
17
34
|
#
|
18
35
|
# class Customer < ActiveRecord::Base
|
19
|
-
# composed_of :balance, :
|
20
|
-
# composed_of :address, :
|
36
|
+
# composed_of :balance, class_name: "Money", mapping: %w(amount currency)
|
37
|
+
# composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
|
21
38
|
# end
|
22
39
|
#
|
23
40
|
# The customer class now has the following methods to manipulate the value objects:
|
@@ -71,7 +88,7 @@ module ActiveRecord
|
|
71
88
|
# Now it's possible to access attributes from the database through the value objects instead. If
|
72
89
|
# you choose to name the composition the same as the attribute's name, it will be the only way to
|
73
90
|
# access that attribute. That's the case with our +balance+ attribute. You interact with the value
|
74
|
-
# objects just like you would any other attribute
|
91
|
+
# objects just like you would with any other attribute:
|
75
92
|
#
|
76
93
|
# customer.balance = Money.new(20) # sets the Money value object and the attribute
|
77
94
|
# customer.balance # => Money value object
|
@@ -86,6 +103,7 @@ module ActiveRecord
|
|
86
103
|
# customer.address_street = "Hyancintvej"
|
87
104
|
# customer.address_city = "Copenhagen"
|
88
105
|
# customer.address # => Address.new("Hyancintvej", "Copenhagen")
|
106
|
+
#
|
89
107
|
# customer.address = Address.new("May Street", "Chicago")
|
90
108
|
# customer.address_street # => "May Street"
|
91
109
|
# customer.address_city # => "Chicago"
|
@@ -101,13 +119,13 @@ module ActiveRecord
|
|
101
119
|
# ActiveRecord::Base classes are entity objects.
|
102
120
|
#
|
103
121
|
# It's also important to treat the value objects as immutable. Don't allow the Money object to have
|
104
|
-
# its amount changed after creation. Create a new Money object with the new value instead.
|
105
|
-
# is
|
122
|
+
# its amount changed after creation. Create a new Money object with the new value instead. The
|
123
|
+
# <tt>Money#exchange_to</tt> method is an example of this. It returns a new value object instead of changing
|
106
124
|
# its own values. Active Record won't persist value objects that have been changed through means
|
107
125
|
# other than the writer method.
|
108
126
|
#
|
109
127
|
# The immutable requirement is enforced by Active Record by freezing any object assigned as a value
|
110
|
-
# object. Attempting to change it afterwards will result in a
|
128
|
+
# object. Attempting to change it afterwards will result in a +RuntimeError+.
|
111
129
|
#
|
112
130
|
# Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not
|
113
131
|
# keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
|
@@ -116,31 +134,31 @@ module ActiveRecord
|
|
116
134
|
#
|
117
135
|
# By default value objects are initialized by calling the <tt>new</tt> constructor of the value
|
118
136
|
# class passing each of the mapped attributes, in the order specified by the <tt>:mapping</tt>
|
119
|
-
# option, as arguments. If the value class doesn't support this convention then
|
137
|
+
# option, as arguments. If the value class doesn't support this convention then #composed_of allows
|
120
138
|
# a custom constructor to be specified.
|
121
139
|
#
|
122
|
-
# When a new value is assigned to the value object the default assumption is that the new value
|
140
|
+
# When a new value is assigned to the value object, the default assumption is that the new value
|
123
141
|
# is an instance of the value class. Specifying a custom converter allows the new value to be automatically
|
124
142
|
# converted to an instance of value class if necessary.
|
125
143
|
#
|
126
|
-
# For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that
|
127
|
-
#
|
128
|
-
# for the value class is called +create+ and it expects a CIDR address string as a parameter.
|
129
|
-
# values can be assigned to the value object using either another NetAddr::CIDR object, a string
|
144
|
+
# For example, the +NetworkResource+ model has +network_address+ and +cidr_range+ attributes that should be
|
145
|
+
# aggregated using the +NetAddr::CIDR+ value class (http://www.rubydoc.info/gems/netaddr/1.5.0/NetAddr/CIDR).
|
146
|
+
# The constructor for the value class is called +create+ and it expects a CIDR address string as a parameter.
|
147
|
+
# New values can be assigned to the value object using either another +NetAddr::CIDR+ object, a string
|
130
148
|
# or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
|
131
149
|
# these requirements:
|
132
150
|
#
|
133
151
|
# class NetworkResource < ActiveRecord::Base
|
134
152
|
# composed_of :cidr,
|
135
|
-
# :
|
136
|
-
# :
|
137
|
-
# :
|
138
|
-
# :
|
139
|
-
# :
|
153
|
+
# class_name: 'NetAddr::CIDR',
|
154
|
+
# mapping: [ %w(network_address network), %w(cidr_range bits) ],
|
155
|
+
# allow_nil: true,
|
156
|
+
# constructor: Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") },
|
157
|
+
# converter: Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }
|
140
158
|
# end
|
141
159
|
#
|
142
160
|
# # This calls the :constructor
|
143
|
-
# network_resource = NetworkResource.new(:
|
161
|
+
# network_resource = NetworkResource.new(network_address: '192.168.0.1', cidr_range: 24)
|
144
162
|
#
|
145
163
|
# # These assignments will both use the :converter
|
146
164
|
# network_resource.cidr = [ '192.168.2.1', 8 ]
|
@@ -155,11 +173,11 @@ module ActiveRecord
|
|
155
173
|
#
|
156
174
|
# == Finding records by a value object
|
157
175
|
#
|
158
|
-
# Once a
|
176
|
+
# Once a #composed_of relationship is specified for a model, records can be loaded from the database
|
159
177
|
# by specifying an instance of the value object in the conditions hash. The following example
|
160
178
|
# finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD":
|
161
179
|
#
|
162
|
-
# Customer.where(:
|
180
|
+
# Customer.where(balance: Money.new(20, "USD"))
|
163
181
|
#
|
164
182
|
module ClassMethods
|
165
183
|
# Adds reader and writer methods for manipulating a value object:
|
@@ -168,7 +186,7 @@ module ActiveRecord
|
|
168
186
|
# Options are:
|
169
187
|
# * <tt>:class_name</tt> - Specifies the class name of the association. Use it only if that name
|
170
188
|
# can't be inferred from the part id. So <tt>composed_of :address</tt> will by default be linked
|
171
|
-
# to the Address class, but if the real class name is CompanyAddress
|
189
|
+
# to the Address class, but if the real class name is +CompanyAddress+, you'll have to specify it
|
172
190
|
# with this option.
|
173
191
|
# * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value
|
174
192
|
# object. Each mapping is represented as an array where the first item is the name of the
|
@@ -187,20 +205,21 @@ module ActiveRecord
|
|
187
205
|
# * <tt>:converter</tt> - A symbol specifying the name of a class method of <tt>:class_name</tt>
|
188
206
|
# or a Proc that is called when a new value is assigned to the value object. The converter is
|
189
207
|
# passed the single value that is used in the assignment and is only called if the new value is
|
190
|
-
# not an instance of <tt>:class_name</tt>.
|
208
|
+
# not an instance of <tt>:class_name</tt>. If <tt>:allow_nil</tt> is set to true, the converter
|
209
|
+
# can return nil to skip the assignment.
|
191
210
|
#
|
192
211
|
# Option examples:
|
193
|
-
# composed_of :temperature, :
|
194
|
-
# composed_of :balance, :
|
195
|
-
# :
|
196
|
-
# composed_of :address, :
|
212
|
+
# composed_of :temperature, mapping: %w(reading celsius)
|
213
|
+
# composed_of :balance, class_name: "Money", mapping: %w(balance amount),
|
214
|
+
# converter: Proc.new { |balance| balance.to_money }
|
215
|
+
# composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
|
197
216
|
# composed_of :gps_location
|
198
|
-
# composed_of :gps_location, :
|
217
|
+
# composed_of :gps_location, allow_nil: true
|
199
218
|
# composed_of :ip_address,
|
200
|
-
# :
|
201
|
-
# :
|
202
|
-
# :
|
203
|
-
# :
|
219
|
+
# class_name: 'IPAddr',
|
220
|
+
# mapping: %w(ip to_i),
|
221
|
+
# constructor: Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
|
222
|
+
# converter: Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
|
204
223
|
#
|
205
224
|
def composed_of(part_id, options = {})
|
206
225
|
options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter)
|
@@ -216,14 +235,15 @@ module ActiveRecord
|
|
216
235
|
reader_method(name, class_name, mapping, allow_nil, constructor)
|
217
236
|
writer_method(name, class_name, mapping, allow_nil, converter)
|
218
237
|
|
219
|
-
|
238
|
+
reflection = ActiveRecord::Reflection.create(:composed_of, part_id, nil, options, self)
|
239
|
+
Reflection.add_aggregate_reflection self, part_id, reflection
|
220
240
|
end
|
221
241
|
|
222
242
|
private
|
223
243
|
def reader_method(name, class_name, mapping, allow_nil, constructor)
|
224
244
|
define_method(name) do
|
225
|
-
if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? {|
|
226
|
-
attrs = mapping.collect {|
|
245
|
+
if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? {|key, _| !_read_attribute(key).nil? })
|
246
|
+
attrs = mapping.collect {|key, _| _read_attribute(key)}
|
227
247
|
object = constructor.respond_to?(:call) ?
|
228
248
|
constructor.call(*attrs) :
|
229
249
|
class_name.constantize.send(constructor, *attrs)
|
@@ -235,17 +255,22 @@ module ActiveRecord
|
|
235
255
|
|
236
256
|
def writer_method(name, class_name, mapping, allow_nil, converter)
|
237
257
|
define_method("#{name}=") do |part|
|
258
|
+
klass = class_name.constantize
|
259
|
+
|
260
|
+
unless part.is_a?(klass) || converter.nil? || part.nil?
|
261
|
+
part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part)
|
262
|
+
end
|
263
|
+
|
264
|
+
if part.is_a?(Hash)
|
265
|
+
raise ArgumentError unless part.size == part.keys.max
|
266
|
+
part = klass.new(*part.sort.map(&:last))
|
267
|
+
end
|
268
|
+
|
238
269
|
if part.nil? && allow_nil
|
239
|
-
mapping.each { |
|
270
|
+
mapping.each { |key, _| self[key] = nil }
|
240
271
|
@aggregation_cache[name] = nil
|
241
272
|
else
|
242
|
-
|
243
|
-
part = converter.respond_to?(:call) ?
|
244
|
-
converter.call(part) :
|
245
|
-
class_name.constantize.send(converter, part)
|
246
|
-
end
|
247
|
-
|
248
|
-
mapping.each { |pair| self[pair.first] = part.send(pair.last) }
|
273
|
+
mapping.each { |key, value| self[key] = part.send(value) }
|
249
274
|
@aggregation_cache[name] = part.freeze
|
250
275
|
end
|
251
276
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class AssociationRelation < Relation
|
3
|
+
def initialize(klass, table, predicate_builder, association)
|
4
|
+
super(klass, table, predicate_builder)
|
5
|
+
@association = association
|
6
|
+
end
|
7
|
+
|
8
|
+
def proxy_association
|
9
|
+
@association
|
10
|
+
end
|
11
|
+
|
12
|
+
def ==(other)
|
13
|
+
other == records
|
14
|
+
end
|
15
|
+
|
16
|
+
def build(*args, &block)
|
17
|
+
scoping { @association.build(*args, &block) }
|
18
|
+
end
|
19
|
+
alias new build
|
20
|
+
|
21
|
+
def create(*args, &block)
|
22
|
+
scoping { @association.create(*args, &block) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def create!(*args, &block)
|
26
|
+
scoping { @association.create!(*args, &block) }
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def exec_queries
|
32
|
+
super.each { |r| @association.set_inverse_instance r }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|