aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile5
-rw-r--r--actionmailer/CHANGELOG.md2
-rw-r--r--actionmailer/lib/action_mailer/base.rb11
-rw-r--r--actionmailer/lib/action_mailer/collector.rb2
-rw-r--r--actionmailer/test/base_test.rb13
-rw-r--r--actionpack/CHANGELOG.md20
-rw-r--r--actionpack/actionpack.gemspec2
-rw-r--r--actionpack/lib/abstract_controller/collector.rb4
-rw-r--r--actionpack/lib/abstract_controller/translation.rb8
-rw-r--r--actionpack/lib/action_controller/caching/sweeping.rb9
-rw-r--r--actionpack/lib/action_controller/metal/flash.rb33
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb14
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb3
-rw-r--r--actionpack/lib/action_controller/metal/rack_delegation.rb10
-rw-r--r--actionpack/lib/action_controller/metal/responder.rb2
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb3
-rw-r--r--actionpack/lib/action_controller/metal/testing.rb31
-rw-r--r--actionpack/lib/action_controller/test_case.rb67
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb18
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/debug_exceptions.rb18
-rw-r--r--actionpack/lib/action_dispatch/middleware/head.rb18
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb10
-rw-r--r--actionpack/lib/action_dispatch/routing/inspector.rb (renamed from railties/lib/rails/application/routes_inspector.rb)4
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb30
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb11
-rw-r--r--actionpack/lib/action_dispatch/testing/test_request.rb8
-rw-r--r--actionpack/lib/action_view/context.rb4
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb8
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb58
-rw-r--r--actionpack/lib/action_view/helpers/tags/base.rb4
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb72
-rw-r--r--actionpack/lib/action_view/lookup_context.rb7
-rw-r--r--actionpack/lib/action_view/renderer/abstract_renderer.rb6
-rw-r--r--actionpack/lib/action_view/renderer/partial_renderer.rb2
-rw-r--r--actionpack/lib/action_view/renderer/template_renderer.rb5
-rw-r--r--actionpack/test/abstract/translation_test.rb13
-rw-r--r--actionpack/test/abstract_unit.rb2
-rw-r--r--actionpack/test/controller/filters_test.rb11
-rw-r--r--actionpack/test/controller/flash_test.rb27
-rw-r--r--actionpack/test/controller/http_token_authentication_test.rb8
-rw-r--r--actionpack/test/controller/mime_responds_test.rb4
-rw-r--r--actionpack/test/controller/new_base/render_action_test.rb8
-rw-r--r--actionpack/test/controller/new_base/render_template_test.rb2
-rw-r--r--actionpack/test/controller/new_base/render_text_test.rb2
-rw-r--r--actionpack/test/controller/test_case_test.rb9
-rw-r--r--actionpack/test/dispatch/mapper_test.rb9
-rw-r--r--actionpack/test/dispatch/mime_type_test.rb14
-rw-r--r--actionpack/test/dispatch/request_test.rb8
-rw-r--r--actionpack/test/dispatch/routing/inspector_test.rb170
-rw-r--r--actionpack/test/dispatch/routing_test.rb52
-rw-r--r--actionpack/test/fixtures/project.rb2
-rw-r--r--actionpack/test/fixtures/reply.rb2
-rw-r--r--actionpack/test/fixtures/test/_changing_priority.html.erb1
-rw-r--r--actionpack/test/fixtures/test/_changing_priority.json.erb1
-rw-r--r--actionpack/test/fixtures/test/_first_json_partial.json.erb1
-rw-r--r--actionpack/test/fixtures/test/_json_change_priority.json.erb0
-rw-r--r--actionpack/test/fixtures/test/_second_json_partial.json.erb1
-rw-r--r--actionpack/test/fixtures/test/change_priorty.html.erb2
-rw-r--r--actionpack/test/fixtures/test/html_template.html.erb1
-rw-r--r--actionpack/test/template/active_model_helper_test.rb14
-rw-r--r--actionpack/test/template/capture_helper_test.rb18
-rw-r--r--actionpack/test/template/erb/tag_helper_test.rb3
-rw-r--r--actionpack/test/template/form_options_helper_test.rb28
-rw-r--r--actionpack/test/template/form_tag_helper_test.rb48
-rw-r--r--actionpack/test/template/render_test.rb10
-rw-r--r--actionpack/test/template/test_case_test.rb12
-rw-r--r--actionpack/test/template/url_helper_test.rb99
-rw-r--r--activemodel/lib/active_model/conversion.rb18
-rw-r--r--activemodel/lib/active_model/mass_assignment_security.rb6
-rw-r--r--activemodel/lib/active_model/observing.rb5
-rw-r--r--activemodel/lib/active_model/serialization.rb50
-rw-r--r--activemodel/lib/active_model/serializers/json.rb8
-rw-r--r--activemodel/lib/active_model/validations/acceptance.rb24
-rw-r--r--activemodel/lib/active_model/validations/clusivity.rb11
-rw-r--r--activemodel/lib/active_model/validations/confirmation.rb27
-rw-r--r--activemodel/lib/active_model/validations/exclusion.rb38
-rw-r--r--activemodel/lib/active_model/validations/format.rb43
-rw-r--r--activemodel/lib/active_model/validations/inclusion.rb44
-rw-r--r--activemodel/lib/active_model/validations/length.rb54
-rw-r--r--activemodel/lib/active_model/validations/numericality.rb52
-rw-r--r--activemodel/lib/active_model/validations/presence.rb23
-rw-r--r--activemodel/lib/active_model/validations/validates.rb23
-rw-r--r--activemodel/lib/active_model/validator.rb4
-rw-r--r--activemodel/test/cases/validations/callbacks_test.rb4
-rw-r--r--activemodel/test/cases/validations/exclusion_validation_test.rb10
-rw-r--r--activemodel/test/cases/validations/i18n_validation_test.rb22
-rw-r--r--activemodel/test/cases/validations/inclusion_validation_test.rb10
-rw-r--r--activerecord/CHANGELOG.md104
-rw-r--r--activerecord/RUNNING_UNIT_TESTS11
-rw-r--r--activerecord/lib/active_record/associations.rb60
-rw-r--r--activerecord/lib/active_record/associations/association.rb21
-rw-r--r--activerecord/lib/active_record/associations/association_scope.rb57
-rw-r--r--activerecord/lib/active_record/associations/builder/association.rb49
-rw-r--r--activerecord/lib/active_record/associations/builder/belongs_to.rb8
-rw-r--r--activerecord/lib/active_record/associations/builder/collection_association.rb33
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb8
-rw-r--r--activerecord/lib/active_record/associations/builder/has_many.rb8
-rw-r--r--activerecord/lib/active_record/associations/builder/has_one.rb19
-rw-r--r--activerecord/lib/active_record/associations/builder/singular_association.rb4
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb98
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb6
-rw-r--r--activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb39
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb10
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_association.rb33
-rw-r--r--activerecord/lib/active_record/associations/preloader.rb19
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb49
-rw-r--r--activerecord/lib/active_record/associations/preloader/collection_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/preloader/has_many_through.rb2
-rw-r--r--activerecord/lib/active_record/associations/preloader/has_one.rb2
-rw-r--r--activerecord/lib/active_record/associations/preloader/through_association.rb27
-rw-r--r--activerecord/lib/active_record/associations/through_association.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb2
-rw-r--r--activerecord/lib/active_record/autosave_association.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb34
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb55
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb235
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb10
-rw-r--r--activerecord/lib/active_record/core.rb2
-rw-r--r--activerecord/lib/active_record/inheritance.rb33
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb2
-rw-r--r--activerecord/lib/active_record/migration/command_recorder.rb14
-rw-r--r--activerecord/lib/active_record/migration/join_table.rb6
-rw-r--r--activerecord/lib/active_record/model.rb6
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb4
-rw-r--r--activerecord/lib/active_record/null_relation.rb3
-rw-r--r--activerecord/lib/active_record/observer.rb6
-rw-r--r--activerecord/lib/active_record/persistence.rb61
-rw-r--r--activerecord/lib/active_record/query_cache.rb14
-rw-r--r--activerecord/lib/active_record/querying.rb20
-rw-r--r--activerecord/lib/active_record/railties/databases.rake24
-rw-r--r--activerecord/lib/active_record/reflection.rb49
-rw-r--r--activerecord/lib/active_record/relation.rb72
-rw-r--r--activerecord/lib/active_record/relation/batches.rb14
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb2
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb15
-rw-r--r--activerecord/lib/active_record/relation/merger.rb4
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb134
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb1
-rw-r--r--activerecord/lib/active_record/sanitization.rb11
-rw-r--r--activerecord/lib/active_record/schema_migration.rb11
-rw-r--r--activerecord/lib/active_record/scoping/default.rb8
-rw-r--r--activerecord/lib/active_record/scoping/named.rb19
-rw-r--r--activerecord/lib/active_record/session_store.rb4
-rw-r--r--activerecord/lib/active_record/store.rb29
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb18
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb2
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/migration_generator.rb31
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/templates/migration.rb26
-rw-r--r--activerecord/lib/rails/generators/active_record/model/templates/module.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/connection_test.rb5
-rw-r--r--activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb4
-rw-r--r--activerecord/test/cases/adapters/mysql/reserved_word_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/reserved_word_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb72
-rw-r--r--activerecord/test/cases/associations/association_scope_test.rb15
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb20
-rw-r--r--activerecord/test/cases/associations/cascaded_eager_loading_test.rb44
-rw-r--r--activerecord/test/cases/associations/eager_load_nested_include_test.rb4
-rw-r--r--activerecord/test/cases/associations/eager_singularization_test.rb14
-rw-r--r--activerecord/test/cases/associations/eager_test.rb262
-rw-r--r--activerecord/test/cases/associations/extension_test.rb6
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb91
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb198
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb15
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb8
-rw-r--r--activerecord/test/cases/associations/has_one_through_associations_test.rb30
-rw-r--r--activerecord/test/cases/associations/inner_join_association_test.rb8
-rw-r--r--activerecord/test/cases/associations/inverse_associations_test.rb18
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb70
-rw-r--r--activerecord/test/cases/associations_test.rb16
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb10
-rw-r--r--activerecord/test/cases/autosave_association_test.rb18
-rw-r--r--activerecord/test/cases/base_test.rb100
-rw-r--r--activerecord/test/cases/batches_test.rb2
-rw-r--r--activerecord/test/cases/calculations_test.rb36
-rw-r--r--activerecord/test/cases/column_test.rb6
-rw-r--r--activerecord/test/cases/custom_locking_test.rb2
-rw-r--r--activerecord/test/cases/deprecated_dynamic_methods_test.rb30
-rw-r--r--activerecord/test/cases/dirty_test.rb2
-rw-r--r--activerecord/test/cases/explain_test.rb18
-rw-r--r--activerecord/test/cases/finder_test.rb148
-rw-r--r--activerecord/test/cases/helper.rb1
-rw-r--r--activerecord/test/cases/inheritance_test.rb58
-rw-r--r--activerecord/test/cases/log_subscriber_test.rb12
-rw-r--r--activerecord/test/cases/migration/change_schema_test.rb4
-rw-r--r--activerecord/test/cases/migration/change_table_test.rb28
-rw-r--r--activerecord/test/cases/migration/column_attributes_test.rb8
-rw-r--r--activerecord/test/cases/migration/command_recorder_test.rb30
-rw-r--r--activerecord/test/cases/migration/create_join_table_test.rb20
-rw-r--r--activerecord/test/cases/migration/helper.rb2
-rw-r--r--activerecord/test/cases/migration/references_statements_test.rb111
-rw-r--r--activerecord/test/cases/migration/rename_column_test.rb4
-rw-r--r--activerecord/test/cases/migration/rename_table_test.rb34
-rw-r--r--activerecord/test/cases/modules_test.rb8
-rw-r--r--activerecord/test/cases/named_scope_test.rb60
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb2
-rw-r--r--activerecord/test/cases/persistence_test.rb152
-rw-r--r--activerecord/test/cases/query_cache_test.rb22
-rw-r--r--activerecord/test/cases/quoting_test.rb8
-rw-r--r--activerecord/test/cases/readonly_test.rb12
-rw-r--r--activerecord/test/cases/reflection_test.rb90
-rw-r--r--activerecord/test/cases/relation_scoping_test.rb58
-rw-r--r--activerecord/test/cases/relation_test.rb7
-rw-r--r--activerecord/test/cases/relations_test.rb185
-rw-r--r--activerecord/test/cases/serialization_test.rb7
-rw-r--r--activerecord/test/cases/session_store/sql_bypass_test.rb14
-rw-r--r--activerecord/test/cases/store_test.rb12
-rw-r--r--activerecord/test/cases/tasks/database_tasks_test.rb16
-rw-r--r--activerecord/test/cases/timestamp_test.rb6
-rw-r--r--activerecord/test/cases/validations/uniqueness_validation_test.rb18
-rw-r--r--activerecord/test/cases/xml_serialization_test.rb2
-rw-r--r--activerecord/test/config.example.yml2
-rw-r--r--activerecord/test/models/author.rb72
-rw-r--r--activerecord/test/models/book.rb2
-rw-r--r--activerecord/test/models/bulb.rb2
-rw-r--r--activerecord/test/models/car.rb6
-rw-r--r--activerecord/test/models/category.rb18
-rw-r--r--activerecord/test/models/comment.rb6
-rw-r--r--activerecord/test/models/company.rb67
-rw-r--r--activerecord/test/models/company_in_module.rb9
-rw-r--r--activerecord/test/models/developer.rb30
-rw-r--r--activerecord/test/models/liquid.rb2
-rw-r--r--activerecord/test/models/member.rb6
-rw-r--r--activerecord/test/models/person.rb12
-rw-r--r--activerecord/test/models/pirate.rb6
-rw-r--r--activerecord/test/models/post.rb42
-rw-r--r--activerecord/test/models/project.rb29
-rw-r--r--activerecord/test/models/sponsor.rb4
-rw-r--r--activerecord/test/models/tagging.rb5
-rw-r--r--activerecord/test/models/topic.rb6
-rw-r--r--activerecord/test/schema/schema.rb6
-rw-r--r--activesupport/CHANGELOG.md2
-rw-r--r--activesupport/activesupport.gemspec1
-rw-r--r--activesupport/lib/active_support/callbacks.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/date.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/date_time.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/object/try.rb17
-rw-r--r--activesupport/lib/active_support/core_ext/time.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/time/zones.rb11
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb102
-rw-r--r--activesupport/lib/active_support/json/encoding.rb63
-rw-r--r--activesupport/lib/active_support/tagged_logging.rb4
-rw-r--r--activesupport/lib/active_support/test_case.rb32
-rw-r--r--activesupport/lib/active_support/testing/declarative.rb40
-rw-r--r--activesupport/lib/active_support/testing/mocha_module.rb22
-rw-r--r--activesupport/lib/active_support/testing/mochaing.rb7
-rw-r--r--activesupport/lib/active_support/testing/setup_and_teardown.rb36
-rw-r--r--activesupport/test/abstract_unit.rb2
-rw-r--r--activesupport/test/core_ext/object_and_class_ext_test.rb30
-rw-r--r--activesupport/test/multibyte_chars_test.rb2
-rwxr-xr-xci/travis.rb3
-rw-r--r--guides/code/getting_started/app/views/comments/_comment.html.erb4
-rw-r--r--guides/code/getting_started/app/views/posts/index.html.erb2
-rw-r--r--guides/code/getting_started/test/test_helper.rb2
-rw-r--r--guides/source/2_2_release_notes.textile4
-rw-r--r--guides/source/2_3_release_notes.textile2
-rw-r--r--guides/source/4_0_release_notes.textile47
-rw-r--r--guides/source/action_mailer_basics.textile4
-rw-r--r--guides/source/active_record_querying.textile45
-rw-r--r--guides/source/active_record_validations_callbacks.textile2
-rw-r--r--guides/source/active_support_core_extensions.textile22
-rw-r--r--guides/source/ajax_on_rails.textile9
-rw-r--r--guides/source/association_basics.textile34
-rw-r--r--guides/source/configuring.textile2
-rw-r--r--guides/source/contributing_to_ruby_on_rails.textile27
-rw-r--r--guides/source/debugging_rails_applications.textile6
-rw-r--r--guides/source/engines.textile38
-rw-r--r--guides/source/form_helpers.textile135
-rw-r--r--guides/source/getting_started.textile18
-rw-r--r--guides/source/i18n.textile2
-rw-r--r--guides/source/layouts_and_rendering.textile2
-rw-r--r--guides/source/migrations.textile47
-rw-r--r--guides/source/plugins.textile2
-rw-r--r--guides/source/security.textile2
-rw-r--r--guides/source/testing.textile88
-rw-r--r--railties/CHANGELOG.md11
-rw-r--r--railties/lib/rails/application.rb8
-rw-r--r--railties/lib/rails/engine.rb3
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/index.html.erb2
-rw-r--r--railties/lib/rails/generators/generated_attribute.rb13
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/test/test_helper.rb2
-rw-r--r--railties/lib/rails/generators/rails/model/USAGE49
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/gitignore2
-rw-r--r--railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb2
-rw-r--r--railties/lib/rails/info_controller.rb4
-rw-r--r--railties/lib/rails/queueing.rb34
-rw-r--r--railties/lib/rails/tasks/routes.rake4
-rw-r--r--railties/lib/rails/test_help.rb5
-rw-r--r--railties/test/application/middleware_test.rb2
-rw-r--r--railties/test/application/queue_test.rb95
-rw-r--r--railties/test/application/rack/logger_test.rb8
-rw-r--r--railties/test/application/routes_inspect_test.rb168
-rw-r--r--railties/test/generators/migration_generator_test.rb42
-rw-r--r--railties/test/generators/namespaced_generators_test.rb2
-rw-r--r--railties/test/generators/plugin_new_generator_test.rb8
-rw-r--r--railties/test/generators_test.rb3
-rw-r--r--railties/test/queueing/container_test.rb30
-rw-r--r--railties/test/queueing/test_queue_test.rb85
-rw-r--r--railties/test/railties/generators_test.rb12
312 files changed, 4581 insertions, 2830 deletions
diff --git a/Gemfile b/Gemfile
index 8ffccf50e1..0ce4541074 100644
--- a/Gemfile
+++ b/Gemfile
@@ -5,11 +5,10 @@ gemspec
if ENV['AREL']
gem 'arel', path: ENV['AREL']
else
- gem 'arel'
+ gem 'arel', github: 'rails/arel'
end
-gem 'minitest', '~> 3.2.0'
-gem 'mocha', '>= 0.11.2'
+gem 'mocha', '>= 0.11.2', :require => false
gem 'rack-test', github: "brynary/rack-test"
gem 'bcrypt-ruby', '~> 3.0.0'
gem 'jquery-rails'
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index 4d8f739403..96cfb43e0b 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,5 +1,7 @@
## Rails 4.0.0 (unreleased) ##
+* Allow to set default Action Mailer options via `config.action_mailer.default_options=` *Robert Pankowecki*
+
* Raise an `ActionView::MissingTemplate` exception when no implicit template could be found. *Damien Mathieu*
* Asynchronously send messages via the Rails Queue *Brian Cardarella*
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index f31e1e007b..150d435140 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -278,6 +278,11 @@ module ActionMailer #:nodoc:
# set something in the defaults using a proc, and then set the same thing inside of your
# mailer method, it will get over written by the mailer method.
#
+ # It is also possible to set these default options that will be used in all mailers through
+ # the <tt>default_options=</tt> configuration in <tt>config/application.rb</tt>:
+ #
+ # config.action_mailer.default_options = { from: "no-reply@example.org" }
+ #
# = Callbacks
#
# You can specify callbacks using before_filter and after_filter for configuring your messages.
@@ -421,6 +426,10 @@ module ActionMailer #:nodoc:
self.default_params = default_params.merge(value).freeze if value
default_params
end
+ # Allows to set defaults through app configuration:
+ #
+ # config.action_mailer.default_options = { from: "no-reply@example.org" }
+ alias :default_options= :default
# Receives a raw email, parses it into an email object, decodes it,
# instantiates a new mailer, and passes the email object to the mailer
@@ -786,4 +795,4 @@ module ActionMailer #:nodoc:
ActiveSupport.run_load_hooks(:action_mailer, self)
end
-end \ No newline at end of file
+end
diff --git a/actionmailer/lib/action_mailer/collector.rb b/actionmailer/lib/action_mailer/collector.rb
index 17b22aea2a..b8d1db9558 100644
--- a/actionmailer/lib/action_mailer/collector.rb
+++ b/actionmailer/lib/action_mailer/collector.rb
@@ -15,7 +15,7 @@ module ActionMailer #:nodoc:
def any(*args, &block)
options = args.extract_options!
- raise "You have to supply at least one format" if args.empty?
+ raise ArgumentError, "You have to supply at least one format" if args.empty?
args.each { |type| send(type, options.dup, &block) }
end
alias :all :any
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index 144a6bfe39..4ed332d13d 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -653,6 +653,19 @@ class BaseTest < ActiveSupport::TestCase
assert_equal "Anonymous mailer body", mailer.welcome.body.encoded.strip
end
+ test "default_from can be set" do
+ class DefaultFromMailer < ActionMailer::Base
+ default :to => 'system@test.lindsaar.net'
+ self.default_options = {from: "robert.pankowecki@gmail.com"}
+
+ def welcome
+ mail(subject: "subject", body: "hello world")
+ end
+ end
+
+ assert_equal ["robert.pankowecki@gmail.com"], DefaultFromMailer.welcome.from
+ end
+
protected
# Execute the block setting the given values and restoring old values after
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 7edba84ff6..ec85f67e58 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,23 @@
## Rails 4.0.0 (unreleased) ##
+* Remove ActionDispatch::Head middleware in favor of Rack::Head. *Santiago Pastorino*
+
+* Deprecate `:confirm` in favor of `:data => { :confirm => "Text" }` option for `button_to`, `button_tag`, `image_submit_tag`, `link_to` and `submit_tag` helpers.
+
+ *Carlos Galdino + Rafael Mendonça França*
+
+* Show routes in exception page while debugging a `RoutingError` in development. *Richard Schneeman and Mattt Thompson*
+
+* Add `ActionController::Flash.add_flash_types` method to allow people to register their own flash types. e.g.:
+
+ class ApplicationController
+ add_flash_types :error, :warning
+ end
+
+ If you add the above code, you can use `<%= error %>` in an erb, and `redirect_to /foo, :error => 'message'` in a controller.
+
+ *kennyj*
+
* Remove Active Model dependency from Action Pack. *Guillermo Iguaran*
* Support unicode characters in routes. Route will be automatically escaped, so instead of manually escaping:
@@ -79,7 +97,7 @@
* Templates without a handler extension now raises a deprecation warning but still
defaults to ERb. In future releases, it will simply return the template contents. *Steve Klabnik*
-* Remove `:disable_with` in favor of `'data-disable-with'` option from `submit_tag`, `button_tag` and `button_to` helpers.
+* Deprecate `:disable_with` in favor of `:data => { :disable_with => "Text" }` option from `submit_tag`, `button_tag` and `button_to` helpers.
*Carlos Galdino + Rafael Mendonça França*
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index 6075e2f02b..dde51da497 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
s.add_dependency('builder', '~> 3.0.0')
s.add_dependency('rack', '~> 1.4.1')
s.add_dependency('rack-test', '~> 0.6.1')
- s.add_dependency('journey', '~> 1.0.1')
+ s.add_dependency('journey', '~> 2.0.0')
s.add_dependency('erubis', '~> 2.7.0')
s.add_development_dependency('activemodel', version)
diff --git a/actionpack/lib/abstract_controller/collector.rb b/actionpack/lib/abstract_controller/collector.rb
index 492329c401..09b9e7ddf0 100644
--- a/actionpack/lib/abstract_controller/collector.rb
+++ b/actionpack/lib/abstract_controller/collector.rb
@@ -16,6 +16,10 @@ module AbstractController
generate_method_for_mime(mime)
end
+ Mime::Type.register_callback do |mime|
+ generate_method_for_mime(mime) unless self.instance_methods.include?(mime.to_sym)
+ end
+
protected
def method_missing(symbol, &block)
diff --git a/actionpack/lib/abstract_controller/translation.rb b/actionpack/lib/abstract_controller/translation.rb
index 6d68cf4944..b6c484d188 100644
--- a/actionpack/lib/abstract_controller/translation.rb
+++ b/actionpack/lib/abstract_controller/translation.rb
@@ -1,6 +1,12 @@
module AbstractController
module Translation
def translate(*args)
+ key = args.first
+ if key.is_a?(String) && (key[0] == '.')
+ key = "#{ controller_path.gsub('/', '.') }.#{ action_name }#{ key }"
+ args[0] = key
+ end
+
I18n.translate(*args)
end
alias :t :translate
@@ -10,4 +16,4 @@ module AbstractController
end
alias :l :localize
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/caching/sweeping.rb b/actionpack/lib/action_controller/caching/sweeping.rb
index 39da15e26a..73291ce083 100644
--- a/actionpack/lib/action_controller/caching/sweeping.rb
+++ b/actionpack/lib/action_controller/caching/sweeping.rb
@@ -68,14 +68,14 @@ module ActionController #:nodoc:
def after(controller)
self.controller = controller
callback(:after) if controller.perform_caching
- # Clean up, so that the controller can be collected after this request
- self.controller = nil
end
def around(controller)
before(controller)
yield
after(controller)
+ ensure
+ clean_up
end
protected
@@ -90,6 +90,11 @@ module ActionController #:nodoc:
end
private
+ def clean_up
+ # Clean up, so that the controller can be collected after this request
+ self.controller = nil
+ end
+
def callback(timing)
controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}"
action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}"
diff --git a/actionpack/lib/action_controller/metal/flash.rb b/actionpack/lib/action_controller/metal/flash.rb
index bd768b634e..b078beb675 100644
--- a/actionpack/lib/action_controller/metal/flash.rb
+++ b/actionpack/lib/action_controller/metal/flash.rb
@@ -3,19 +3,34 @@ module ActionController #:nodoc:
extend ActiveSupport::Concern
included do
- delegate :flash, :to => :request
- delegate :alert, :notice, :to => "request.flash"
- helper_method :alert, :notice
+ class_attribute :_flash_types, instance_accessor: false
+ self._flash_types = []
+
+ delegate :flash, to: :request
+ add_flash_types(:alert, :notice)
end
- protected
- def redirect_to(options = {}, response_status_and_flash = {}) #:doc:
- if alert = response_status_and_flash.delete(:alert)
- flash[:alert] = alert
+ module ClassMethods
+ def add_flash_types(*types)
+ types.each do |type|
+ next if _flash_types.include?(type)
+
+ define_method(type) do
+ request.flash[type]
+ end
+ helper_method type
+
+ _flash_types << type
end
+ end
+ end
- if notice = response_status_and_flash.delete(:notice)
- flash[:notice] = notice
+ protected
+ def redirect_to(options = {}, response_status_and_flash = {}) #:doc:
+ self.class._flash_types.each do |flash_type|
+ if type = response_status_and_flash.delete(flash_type)
+ flash[flash_type] = type
+ end
end
if other_flashes = response_status_and_flash.delete(:flash)
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index a0d1064094..d84588d3df 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -229,9 +229,9 @@ module ActionController
end
def decode_credentials(header)
- Hash[header.to_s.gsub(/^Digest\s+/,'').split(',').map do |pair|
+ HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/,'').split(',').map do |pair|
key, value = pair.split('=', 2)
- [key.strip.to_sym, value.to_s.gsub(/^"|"$/,'').delete('\'')]
+ [key.strip, value.to_s.gsub(/^"|"$/,'').delete('\'')]
end]
end
@@ -436,10 +436,12 @@ module ActionController
values = Hash[$1.split(',').map do |value|
value.strip! # remove any spaces between commas and values
key, value = value.split(/\=\"?/) # split key=value pairs
- value.chomp!('"') # chomp trailing " in value
- value.gsub!(/\\\"/, '"') # unescape remaining quotes
- [key, value]
- end]
+ if value
+ value.chomp!('"') # chomp trailing " in value
+ value.gsub!(/\\\"/, '"') # unescape remaining quotes
+ [key, value]
+ end
+ end.compact]
[values.delete("token"), values.with_indifferent_access]
end
end
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 0b800c3c62..4665fea91a 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -182,7 +182,8 @@ module ActionController #:nodoc:
# end
# end
#
- # Be sure to check respond_with and respond_to documentation for more examples.
+ # Be sure to check the documentation of +respond_with+ and
+ # <tt>ActionController::MimeResponds.respond_to</tt> for more examples.
def respond_to(*mimes, &block)
raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
diff --git a/actionpack/lib/action_controller/metal/rack_delegation.rb b/actionpack/lib/action_controller/metal/rack_delegation.rb
index f01f4b99a9..bdf6e88699 100644
--- a/actionpack/lib/action_controller/metal/rack_delegation.rb
+++ b/actionpack/lib/action_controller/metal/rack_delegation.rb
@@ -9,8 +9,7 @@ module ActionController
:status, :location, :content_type, :to => "@_response"
def dispatch(action, request)
- @_response = ActionDispatch::Response.new
- @_response.request = request
+ set_response!(request)
super(action, request)
end
@@ -22,5 +21,12 @@ module ActionController
def reset_session
@_request.reset_session
end
+
+ private
+
+ def set_response!(request)
+ @_response = ActionDispatch::Response.new
+ @_response.request = request
+ end
end
end
diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb
index 83407846dc..d9c89a74f1 100644
--- a/actionpack/lib/action_controller/metal/responder.rb
+++ b/actionpack/lib/action_controller/metal/responder.rb
@@ -90,7 +90,7 @@ module ActionController #:nodoc:
#
# def create
# @project = Project.find(params[:project_id])
- # @task = @project.comments.build(params[:task])
+ # @task = @project.tasks.build(params[:task])
# flash[:notice] = 'Task was successfully created.' if @task.save
# respond_with(@project, @task, :status => 201)
# end
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index eeb37db2e7..9f3c997024 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -139,9 +139,6 @@ module ActionController #:nodoc:
# session or flash after the template starts rendering will not propagate
# to the client.
#
- # If you try to modify cookies, session or flash, an <tt>ActionDispatch::ClosedError</tt>
- # will be raised, showing those objects are closed for modification.
- #
# == Middlewares
#
# Middlewares that need to manipulate the body won't work with streaming.
diff --git a/actionpack/lib/action_controller/metal/testing.rb b/actionpack/lib/action_controller/metal/testing.rb
index d1813ee745..0377b8c4cf 100644
--- a/actionpack/lib/action_controller/metal/testing.rb
+++ b/actionpack/lib/action_controller/metal/testing.rb
@@ -4,30 +4,25 @@ module ActionController
include RackDelegation
- def recycle!
- @_url_options = nil
- end
-
-
- # TODO: Clean this up
- def process_with_new_base_test(request, response)
- @_request = request
- @_response = response
- @_response.request = request
- ret = process(request.parameters[:action])
- if cookies = @_request.env['action_dispatch.cookies']
- cookies.write(@_response)
- end
- @_response.prepare!
- ret
- end
-
# TODO : Rewrite tests using controller.headers= to use Rack env
def headers=(new_headers)
@_response ||= ActionDispatch::Response.new
@_response.headers.replace(new_headers)
end
+ # Behavior specific to functional tests
+ module Functional # :nodoc:
+ def set_response!(request)
+ end
+
+ def recycle!
+ @_url_options = nil
+ self.response_body = nil
+ self.formats = nil
+ self.params = nil
+ end
+ end
+
module ClassMethods
def before_filters
_process_action_callbacks.find_all{|x| x.kind == :before}.map{|x| x.name}
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index a1f29ea1bc..5f50bf5de6 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -143,6 +143,9 @@ module ActionController
end
class TestRequest < ActionDispatch::TestRequest #:nodoc:
+ DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup
+ DEFAULT_ENV.delete 'PATH_INFO'
+
def initialize(env = {})
super
@@ -150,10 +153,6 @@ module ActionController
self.session_options = TestSession::DEFAULT_OPTIONS.merge(:id => SecureRandom.hex(16))
end
- class Result < ::Array #:nodoc:
- def to_s() join '/' end
- end
-
def assign_parameters(routes, controller_path, action, parameters = {})
parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action)
extra_keys = routes.extra_keys(parameters)
@@ -171,7 +170,7 @@ module ActionController
non_path_parameters[key] = value
else
if value.is_a?(Array)
- value = Result.new(value.map(&:to_param))
+ value = value.map(&:to_param)
else
value = value.to_param
end
@@ -211,6 +210,12 @@ module ActionController
cookie_jar.update(@set_cookies)
cookie_jar.recycle!
end
+
+ private
+
+ def default_env
+ DEFAULT_ENV
+ end
end
class TestResponse < ActionDispatch::TestResponse
@@ -353,7 +358,7 @@ module ActionController
# Use AS::TestCase for the base class when describing a model
register_spec_type(self) do |desc|
- desc < ActionController::Base
+ Class === desc && desc < ActionController::Base
end
module Behavior
@@ -430,8 +435,13 @@ module ActionController
end
# Executes a request simulating HEAD HTTP method and set/volley the response
- def head(action, parameters = nil, session = nil, flash = nil)
- process(action, "HEAD", parameters, session, flash)
+ def head(action, *args)
+ process(action, "HEAD", *args)
+ end
+
+ # Executes a request simulating OPTIONS HTTP method and set/volley the response
+ def options(action, *args)
+ process(action, "OPTIONS", *args)
end
def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
@@ -471,13 +481,17 @@ module ActionController
# proper params, as is the case when engaging rack.
parameters = paramify_values(parameters) if html_format?(parameters)
+ @html_document = nil
+
+ unless @controller.respond_to?(:recycle!)
+ @controller.extend(Testing::Functional)
+ @controller.class.class_eval { include Testing }
+ end
+
@request.recycle!
@response.recycle!
- @controller.response_body = nil
- @controller.formats = nil
- @controller.params = nil
+ @controller.recycle!
- @html_document = nil
@request.env['REQUEST_METHOD'] = http_method
parameters ||= {}
@@ -490,26 +504,34 @@ module ActionController
@request.session.update(session) if session
@request.session["flash"] = @request.flash.update(flash || {})
- @controller.request = @request
+ @controller.request = @request
+ @controller.response = @response
+
build_request_uri(action, parameters)
- @controller.class.class_eval { include Testing }
- @controller.recycle!
- @controller.process_with_new_base_test(@request, @response)
+
+ name = @request.parameters[:action]
+
+ @controller.process(name)
+
+ if cookies = @request.env['action_dispatch.cookies']
+ cookies.write(@response)
+ end
+ @response.prepare!
+
@assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {}
@request.session.delete('flash') if @request.session['flash'].blank?
@response
end
def setup_controller_request_and_response
- @request = TestRequest.new
- @response = TestResponse.new
+ @request = TestRequest.new
+ @response = TestResponse.new
+ @response.request = @request
if klass = self.class.controller_class
@controller ||= klass.new rescue nil
end
- @request.env.delete('PATH_INFO')
-
if defined?(@controller) && @controller
@controller.request = @request
@controller.params = {}
@@ -523,7 +545,7 @@ module ActionController
setup :setup_controller_request_and_response
end
- private
+ private
def check_required_ivars
# Sanity check for required instance variables so we can give an
# understandable error message.
@@ -564,8 +586,7 @@ module ActionController
def html_format?(parameters)
return true unless parameters.is_a?(Hash)
- format = Mime[parameters[:format]]
- format.nil? || format.html?
+ Mime.fetch(parameters[:format]) { Mime['html'] }.html?
end
end
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index ee1913dbf9..fe39c220a5 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -29,6 +29,11 @@ module Mime
Type.lookup_by_extension(type.to_s)
end
+ def self.fetch(type)
+ return type if type.is_a?(Type)
+ EXTENSION_LOOKUP.fetch(type.to_s) { |k| yield k }
+ end
+
# Encapsulates the notion of a mime type. Can be used at render time, for example, with:
#
# class PostsController < ActionController::Base
@@ -53,6 +58,8 @@ module Mime
cattr_reader :browser_generated_types
attr_reader :symbol
+ @register_callbacks = []
+
# A simple helper class used in parsing the accept header
class AcceptItem #:nodoc:
attr_accessor :order, :name, :q
@@ -84,6 +91,10 @@ module Mime
TRAILING_STAR_REGEXP = /(text|application)\/\*/
PARAMETER_SEPARATOR_REGEXP = /;\s*\w+="?\w+"?/
+ def register_callback(&block)
+ @register_callbacks << block
+ end
+
def lookup(string)
LOOKUP[string]
end
@@ -101,10 +112,15 @@ module Mime
def register(string, symbol, mime_type_synonyms = [], extension_synonyms = [], skip_lookup = false)
Mime.const_set(symbol.upcase, Type.new(string, symbol, mime_type_synonyms))
- SET << Mime.const_get(symbol.upcase)
+ new_mime = Mime.const_get(symbol.upcase)
+ SET << new_mime
([string] + mime_type_synonyms).each { |str| LOOKUP[str] = SET.last } unless skip_lookup
([symbol] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext.to_s] = SET.last }
+
+ @register_callbacks.each do |callback|
+ callback.call(new_mime)
+ end
end
def parse(accept_header)
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 8cea17c7a6..1377e53ce8 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -119,9 +119,9 @@ module ActionDispatch
end
# Is this a HEAD request?
- # Equivalent to <tt>request.method_symbol == :head</tt>.
+ # Equivalent to <tt>request.request_method_symbol == :head</tt>.
def head?
- HTTP_METHOD_LOOKUP[method] == :head
+ HTTP_METHOD_LOOKUP[request_method] == :head
end
# Provides access to the request's HTTP headers, for example:
diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
index b903f98761..0f0589a844 100644
--- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
@@ -1,5 +1,7 @@
require 'action_dispatch/http/request'
require 'action_dispatch/middleware/exception_wrapper'
+require 'action_dispatch/routing/inspector'
+
module ActionDispatch
# This middleware is responsible for logging exceptions and
@@ -7,8 +9,9 @@ module ActionDispatch
class DebugExceptions
RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates')
- def initialize(app)
- @app = app
+ def initialize(app, routes_app = nil)
+ @app = app
+ @routes_app = routes_app
end
def call(env)
@@ -39,7 +42,8 @@ module ActionDispatch
:exception => wrapper.exception,
:application_trace => wrapper.application_trace,
:framework_trace => wrapper.framework_trace,
- :full_trace => wrapper.full_trace
+ :full_trace => wrapper.full_trace,
+ :routes => formatted_routes(exception)
)
file = "rescues/#{wrapper.rescue_template}"
@@ -78,5 +82,13 @@ module ActionDispatch
def stderr_logger
@stderr_logger ||= ActiveSupport::Logger.new($stderr)
end
+
+ def formatted_routes(exception)
+ return false unless @routes_app.respond_to?(:routes)
+ if exception.is_a?(ActionController::RoutingError) || exception.is_a?(ActionView::Template::Error)
+ inspector = ActionDispatch::Routing::RoutesInspector.new
+ inspector.format(@routes_app.routes.routes).join("\n")
+ end
+ end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/head.rb b/actionpack/lib/action_dispatch/middleware/head.rb
deleted file mode 100644
index f1906a3ab3..0000000000
--- a/actionpack/lib/action_dispatch/middleware/head.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module ActionDispatch
- class Head
- def initialize(app)
- @app = app
- end
-
- def call(env)
- if env["REQUEST_METHOD"] == "HEAD"
- env["REQUEST_METHOD"] = "GET"
- env["rack.methodoverride.original_method"] = "HEAD"
- status, headers, _ = @app.call(env)
- [status, headers, []]
- else
- @app.call(env)
- end
- end
- end
-end
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb
index 177d383e94..8c594c1523 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb
@@ -10,8 +10,14 @@
</ol>
</p>
<% end %>
+<%= render :template => "rescues/_trace" %>
+
+<h2>
+ Routes
+</h2>
+
<p>
- Try running <code>rake routes</code> for more information on available routes.
+ Routes match in priority from top to bottom
</p>
-<%= render :template => "rescues/_trace" %>
+<p><pre><%= @routes %></pre></p>
diff --git a/railties/lib/rails/application/routes_inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb
index 6b2caf8277..bc7229b6a1 100644
--- a/railties/lib/rails/application/routes_inspector.rb
+++ b/actionpack/lib/action_dispatch/routing/inspector.rb
@@ -1,7 +1,7 @@
require 'delegate'
-module Rails
- class Application
+module ActionDispatch
+ module Routing
class RouteWrapper < SimpleDelegator
def endpoint
rack_app ? rack_app.inspect : "#{controller}##{action}"
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 53a4afecb3..0a65b4dbcc 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -262,7 +262,7 @@ module ActionDispatch
# for root cases, where the latter is the correct one.
def self.normalize_path(path)
path = Journey::Router::Utils.normalize_path(path)
- path.gsub!(%r{/(\(+)/?}, '\1/') unless path =~ %r{^/\(+[^/]+\)$}
+ path.gsub!(%r{/(\(+)/?}, '\1/') unless path =~ %r{^/\(+[^)]+\)$}
path
end
@@ -430,6 +430,10 @@ module ActionDispatch
if options
path = options.delete(:at)
else
+ unless Hash === app
+ raise ArgumentError, "must be called with mount point"
+ end
+
options = app
app, path = options.find { |k, v| k.respond_to?(:call) }
options.delete(app) if app
@@ -913,7 +917,7 @@ module ActionDispatch
@path = (options[:path] || @name).to_s
@controller = (options[:controller] || @name).to_s
@as = options[:as]
- @param = options[:param] || :id
+ @param = (options[:param] || :id).to_sym
@options = options
end
@@ -961,12 +965,18 @@ module ActionDispatch
"#{path}/:#{param}"
end
+ alias :shallow_scope :member_scope
+
def new_scope(new_path)
"#{path}/#{new_path}"
end
+ def nested_param
+ :"#{singular}_#{param}"
+ end
+
def nested_scope
- "#{path}/:#{singular}_#{param}"
+ "#{path}/:#{nested_param}"
end
end
@@ -1477,18 +1487,18 @@ module ActionDispatch
def nested_options #:nodoc:
options = { :as => parent_resource.member_name }
options[:constraints] = {
- :"#{parent_resource.singular}_id" => id_constraint
- } if id_constraint?
+ parent_resource.nested_param => param_constraint
+ } if param_constraint?
options
end
- def id_constraint? #:nodoc:
- @scope[:constraints] && @scope[:constraints][:id].is_a?(Regexp)
+ def param_constraint? #:nodoc:
+ @scope[:constraints] && @scope[:constraints][parent_resource.param].is_a?(Regexp)
end
- def id_constraint #:nodoc:
- @scope[:constraints][:id]
+ def param_constraint #:nodoc:
+ @scope[:constraints][parent_resource.param]
end
def canonical_action?(action, flag) #:nodoc:
@@ -1501,7 +1511,7 @@ module ActionDispatch
def path_for_action(action, path) #:nodoc:
prefix = shallow_scoping? ?
- "#{@scope[:shallow_path]}/#{parent_resource.path}/:id" : @scope[:path]
+ "#{@scope[:shallow_path]}/#{parent_resource.shallow_scope}" : @scope[:path]
if canonical_action?(action, path.blank?)
prefix.to_s
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 1d6ca0c78d..0bbed6cbea 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -333,7 +333,7 @@ module ActionDispatch
end
end
- MountedHelpers.class_eval <<-RUBY
+ MountedHelpers.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
def #{name}
@#{name} ||= _#{name}
end
diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb
index fd3bed7e8f..f4c708ea33 100644
--- a/actionpack/lib/action_dispatch/routing/url_for.rb
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -102,7 +102,7 @@ module ActionDispatch
super
end
- # Hook overriden in controller to add request information
+ # Hook overridden in controller to add request information
# with `default_url_options`. Application logic should not
# go into url_options.
def url_options
diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index 41fa3a4b95..9de545b3c5 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -127,16 +127,13 @@ module ActionDispatch
# with a new RouteSet instance.
#
# The new instance is yielded to the passed block. Typically the block
- # will create some routes using <tt>map.draw { map.connect ... }</tt>:
+ # will create some routes using <tt>set.draw { match ... }</tt>:
#
# with_routing do |set|
- # set.draw do |map|
- # map.connect ':controller/:action/:id'
- # assert_equal(
- # ['/content/10/show', {}],
- # map.generate(:controller => 'content', :id => 10, :action => 'show')
- # end
+ # set.draw do
+ # resources :users
# end
+ # assert_equal "/users", users_path
# end
#
def with_routing
diff --git a/actionpack/lib/action_dispatch/testing/test_request.rb b/actionpack/lib/action_dispatch/testing/test_request.rb
index a86b510719..639ae6f398 100644
--- a/actionpack/lib/action_dispatch/testing/test_request.rb
+++ b/actionpack/lib/action_dispatch/testing/test_request.rb
@@ -12,7 +12,7 @@ module ActionDispatch
def initialize(env = {})
env = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
- super(DEFAULT_ENV.merge(env))
+ super(default_env.merge(env))
self.host = 'test.host'
self.remote_addr = '0.0.0.0'
@@ -69,5 +69,11 @@ module ActionDispatch
def cookies
@cookies ||= {}.with_indifferent_access
end
+
+ private
+
+ def default_env
+ DEFAULT_ENV
+ end
end
end
diff --git a/actionpack/lib/action_view/context.rb b/actionpack/lib/action_view/context.rb
index 245849d706..ee263df484 100644
--- a/actionpack/lib/action_view/context.rb
+++ b/actionpack/lib/action_view/context.rb
@@ -26,11 +26,11 @@ module ActionView
# Encapsulates the interaction with the view flow so it
# returns the correct buffer on +yield+. This is usually
- # overwriten by helpers to add more behavior.
+ # overwritten by helpers to add more behavior.
# :api: plugin
def _layout_for(name=nil)
name ||= :layout
view_flow.get(name).html_safe
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 02c1250c76..68b0195700 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -95,7 +95,7 @@ module ActionView
# have SSL certificates for each of the asset hosts this technique allows you
# to avoid warnings in the client about mixed media.
#
- # ActionController::Base.asset_host = Proc.new { |source, request|
+ # config.action_controller.asset_host = Proc.new { |source, request|
# if request.ssl?
# "#{request.protocol}#{request.host_with_port}"
# else
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index 397738dd98..9186855319 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -134,7 +134,7 @@ module ActionView
#
# <%# Add some other content, or use a different template: %>
#
- # <% content_for :navigation, true do %>
+ # <% content_for :navigation, flush: true do %>
# <li><%= link_to 'Login', :action => 'login' %></li>
# <% end %>
#
@@ -148,14 +148,14 @@ module ActionView
#
# WARNING: content_for is ignored in caches. So you shouldn't use it
# for elements that will be fragment cached.
- def content_for(name, content = nil, flush = false, &block)
+ def content_for(name, content = nil, options = {}, &block)
if content || block_given?
if block_given?
- flush = content if content
+ options = content if content
content = capture(&block)
end
if content
- flush ? @view_flow.set(name, content) : @view_flow.append(name, content)
+ options[:flush] ? @view_flow.set(name, content) : @view_flow.append(name, content)
end
nil
else
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index eef426703d..c88af0355f 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -353,7 +353,7 @@ module ActionView
html_attributes[:disabled] = 'disabled' if disabled && option_value_selected?(value, disabled)
html_attributes[:value] = value
- content_tag(:option, text, html_attributes)
+ content_tag_string(:option, text, html_attributes)
end.join("\n").html_safe
end
@@ -711,7 +711,7 @@ module ActionView
def option_html_attributes(element)
return {} unless Array === element
- Hash[element.select { |e| Hash === e }.reduce({}, :merge).map { |k, v| [k, ERB::Util.html_escape(v.to_s)] }]
+ Hash[element.select { |e| Hash === e }.reduce({}, :merge).map { |k, v| [k, v] }]
end
def option_text_and_value(option)
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 1a0019a48c..d7d9c45120 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -382,11 +382,18 @@ module ActionView
# Creates a submit button with the text <tt>value</tt> as the caption.
#
# ==== Options
+ # * <tt>:data</tt> - This option can be used to add custom data attributes.
+ # * <tt>:disabled</tt> - If true, the user will not be able to use this input.
+ # * Any other key creates standard HTML options for the tag.
+ #
+ # ==== Data attributes
+ #
# * <tt>:confirm => 'question?'</tt> - If present the unobtrusive JavaScript
# drivers will provide a prompt with the question specified. If the user accepts,
# the form is processed normally, otherwise no action is taken.
- # * <tt>:disabled</tt> - If true, the user will not be able to use this input.
- # * Any other key creates standard HTML options for the tag.
+ # * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a
+ # disabled version of the submit button when the form is submitted. This feature is
+ # provided by the unobtrusive JavaScript driver.
#
# ==== Examples
# submit_tag
@@ -407,13 +414,21 @@ module ActionView
# submit_tag "Edit", :class => "edit_button"
# # => <input class="edit_button" name="commit" type="submit" value="Edit" />
#
- # submit_tag "Save", :confirm => "Are you sure?"
+ # submit_tag "Save", :data => { :confirm => "Are you sure?" }
# # => <input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />
#
def submit_tag(value = "Save changes", options = {})
options = options.stringify_keys
+ if disable_with = options.delete("disable_with")
+ ActiveSupport::Deprecation.warn ":disable_with option is deprecated and will be removed from Rails 4.1. Use ':data => { :disable_with => \'Text\' }' instead"
+
+ options["data-disable-with"] = disable_with
+ end
+
if confirm = options.delete("confirm")
+ ActiveSupport::Deprecation.warn ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead'"
+
options["data-confirm"] = confirm
end
@@ -428,13 +443,21 @@ module ActionView
# so this helper will also accept a block.
#
# ==== Options
+ # * <tt>:data</tt> - This option can be used to add custom data attributes.
+ # * <tt>:disabled</tt> - If true, the user will not be able to
+ # use this input.
+ # * Any other key creates standard HTML options for the tag.
+ #
+ # ==== Data attributes
+ #
# * <tt>:confirm => 'question?'</tt> - If present, the
# unobtrusive JavaScript drivers will provide a prompt with
# the question specified. If the user accepts, the form is
# processed normally, otherwise no action is taken.
- # * <tt>:disabled</tt> - If true, the user will not be able to
- # use this input.
- # * Any other key creates standard HTML options for the tag.
+ # * <tt>:disable_with</tt> - Value of this parameter will be
+ # used as the value for a disabled version of the submit
+ # button when the form is submitted. This feature is provided
+ # by the unobtrusive JavaScript driver.
#
# ==== Examples
# button_tag
@@ -447,12 +470,23 @@ module ActionView
# # <strong>Ask me!</strong>
# # </button>
#
+ # button_tag "Checkout", :data => { disable_with => "Please wait..." }
+ # # => <button data-disable-with="Please wait..." name="button" type="submit">Checkout</button>
+ #
def button_tag(content_or_options = nil, options = nil, &block)
options = content_or_options if block_given? && content_or_options.is_a?(Hash)
options ||= {}
options = options.stringify_keys
+ if disable_with = options.delete("disable_with")
+ ActiveSupport::Deprecation.warn ":disable_with option is deprecated and will be removed from Rails 4.1. Use ':data => { :disable_with => \'Text\' }' instead"
+
+ options["data-disable-with"] = disable_with
+ end
+
if confirm = options.delete("confirm")
+ ActiveSupport::Deprecation.warn ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead'"
+
options["data-confirm"] = confirm
end
@@ -466,11 +500,15 @@ module ActionView
# <tt>source</tt> is passed to AssetTagHelper#path_to_image
#
# ==== Options
+ # * <tt>:data</tt> - This option can be used to add custom data attributes.
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
+ # * Any other key creates standard HTML options for the tag.
+ #
+ # ==== Data attributes
+ #
# * <tt>:confirm => 'question?'</tt> - This will add a JavaScript confirm
# prompt with the question specified. If the user accepts, the form is
# processed normally, otherwise no action is taken.
- # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
- # * Any other key creates standard HTML options for the tag.
#
# ==== Examples
# image_submit_tag("login.png")
@@ -485,12 +523,14 @@ module ActionView
# image_submit_tag("agree.png", :disabled => true, :class => "agree_disagree_button")
# # => <input class="agree_disagree_button" disabled="disabled" src="/images/agree.png" type="image" />
#
- # image_submit_tag("save.png", :confirm => "Are you sure?")
+ # image_submit_tag("save.png", :data => { :confirm => "Are you sure?" })
# # => <input src="/images/save.png" data-confirm="Are you sure?" type="image" />
def image_submit_tag(source, options = {})
options = options.stringify_keys
if confirm = options.delete("confirm")
+ ActiveSupport::Deprecation.warn ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead'"
+
options["data-confirm"] = confirm
end
diff --git a/actionpack/lib/action_view/helpers/tags/base.rb b/actionpack/lib/action_view/helpers/tags/base.rb
index e077cd5b3c..192f5eebaa 100644
--- a/actionpack/lib/action_view/helpers/tags/base.rb
+++ b/actionpack/lib/action_view/helpers/tags/base.rb
@@ -137,10 +137,10 @@ module ActionView
def add_options(option_tags, options, value = nil)
if options[:include_blank]
- option_tags = content_tag('option', options[:include_blank].kind_of?(String) ? options[:include_blank] : nil, :value => '') + "\n" + option_tags
+ option_tags = content_tag_string('option', options[:include_blank].kind_of?(String) ? options[:include_blank] : nil, :value => '') + "\n" + option_tags
end
if value.blank? && options[:prompt]
- option_tags = content_tag('option', prompt_text(options[:prompt]), :value => '') + "\n" + option_tags
+ option_tags = content_tag_string('option', prompt_text(options[:prompt]), :value => '') + "\n" + option_tags
end
option_tags
end
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 736f9fa2f0..3d86790a8f 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -132,8 +132,7 @@ module ActionView
# # posts_path
#
# link_to(body, url_options = {}, html_options = {})
- # # url_options, except :confirm or :method,
- # # is passed to url_for
+ # # url_options, except :method, is passed to url_for
#
# link_to(options = {}, html_options = {}) do
# # name
@@ -144,9 +143,7 @@ module ActionView
# end
#
# ==== Options
- # * <tt>:confirm => 'question?'</tt> - This will allow the unobtrusive JavaScript
- # driver to prompt with the question specified. If the user accepts, the link is
- # processed normally, otherwise no action is taken.
+ # * <tt>:data</tt> - This option can be used to add custom data attributes.
# * <tt>:method => symbol of HTTP verb</tt> - This modifier will dynamically
# create an HTML form and immediately submit the form for processing using
# the HTTP verb specified. Useful for having links perform a POST operation
@@ -163,6 +160,16 @@ module ActionView
# completion of the Ajax request and performing JavaScript operations once
# they're complete
#
+ # ==== Data attributes
+ #
+ # * <tt>:confirm => 'question?'</tt> - This will allow the unobtrusive JavaScript
+ # driver to prompt with the question specified. If the user accepts, the link is
+ # processed normally, otherwise no action is taken.
+ # * <tt>:disable_with</tt> - Value of this parameter will be
+ # used as the value for a disabled version of the submit
+ # button when the form is submitted. This feature is provided
+ # by the unobtrusive JavaScript driver.
+ #
# ==== Examples
# Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
# and newer RESTful routes. Current Rails style favors RESTful routes whenever possible, so base
@@ -226,13 +233,15 @@ module ActionView
# link_to "Nonsense search", searches_path(:foo => "bar", :baz => "quux")
# # => <a href="/searches?foo=bar&amp;baz=quux">Nonsense search</a>
#
- # The two options specific to +link_to+ (<tt>:confirm</tt> and <tt>:method</tt>) are used as follows:
+ # The only option specific to +link_to+ (<tt>:method</tt>) is used as follows:
#
- # link_to "Visit Other Site", "http://www.rubyonrails.org/", :confirm => "Are you sure?"
- # # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?"">Visit Other Site</a>
+ # link_to("Destroy", "http://www.example.com", :method => :delete)
+ # # => <a href='http://www.example.com' rel="nofollow" data-method="delete">Destroy</a>
#
- # link_to("Destroy", "http://www.example.com", :method => :delete, :confirm => "Are you sure?")
- # # => <a href='http://www.example.com' rel="nofollow" data-method="delete" data-confirm="Are you sure?">Destroy</a>
+ # You can also use custom data attributes using the <tt>:data</tt> option:
+ #
+ # link_to "Visit Other Site", "http://www.rubyonrails.org/", :data => { :confirm => "Are you sure?" }
+ # # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?"">Visit Other Site</a>
def link_to(name = nil, options = nil, html_options = nil, &block)
html_options, options = options, name if block_given?
options ||= {}
@@ -255,10 +264,9 @@ module ActionView
# to allow styling of the form itself and its children. This can be changed
# using the <tt>:form_class</tt> modifier within +html_options+. You can control
# the form submission and input element behavior using +html_options+.
- # This method accepts the <tt>:method</tt> and <tt>:confirm</tt> modifiers
- # described in the +link_to+ documentation. If no <tt>:method</tt> modifier
- # is given, it will default to performing a POST operation. You can also
- # disable the button by passing <tt>:disabled => true</tt> in +html_options+.
+ # This method accepts the <tt>:method</tt> modifier described in the +link_to+ documentation.
+ # If no <tt>:method</tt> modifier is given, it will default to performing a POST operation.
+ # You can also disable the button by passing <tt>:disabled => true</tt> in +html_options+.
# If you are using RESTful routes, you can pass the <tt>:method</tt>
# to change the HTTP verb used to submit the form.
#
@@ -269,15 +277,23 @@ module ActionView
# * <tt>:method</tt> - Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>,
# <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>. By default it will be <tt>:post</tt>.
# * <tt>:disabled</tt> - If set to true, it will generate a disabled button.
- # * <tt>:confirm</tt> - This will use the unobtrusive JavaScript driver to
- # prompt with the question specified. If the user accepts, the link is
- # processed normally, otherwise no action is taken.
+ # * <tt>:data</tt> - This option can be used to add custom data attributes.
# * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
# submit behavior. By default this behavior is an ajax submit.
# * <tt>:form</tt> - This hash will be form attributes
# * <tt>:form_class</tt> - This controls the class of the form within which the submit button will
# be placed
#
+ # ==== Data attributes
+ #
+ # * <tt>:confirm</tt> - This will use the unobtrusive JavaScript driver to
+ # prompt with the question specified. If the user accepts, the link is
+ # processed normally, otherwise no action is taken.
+ # * <tt>:disable_with</tt> - Value of this parameter will be
+ # used as the value for a disabled version of the submit
+ # button when the form is submitted. This feature is provided
+ # by the unobtrusive JavaScript driver.
+ #
# ==== Examples
# <%= button_to "New", :action => "new" %>
# # => "<form method="post" action="/controller/new" class="button_to">
@@ -311,7 +327,7 @@ module ActionView
#
#
# <%= button_to "Delete Image", { :action => "delete", :id => @image.id },
- # :confirm => "Are you sure?", :method => :delete %>
+ # :method => :delete, :data => { :confirm => "Are you sure?" } %>
# # => "<form method="post" action="/images/delete/1" class="button_to">
# # <div>
# # <input type="hidden" name="_method" value="delete" />
@@ -321,12 +337,12 @@ module ActionView
# # </form>"
#
#
- # <%= button_to('Destroy', 'http://www.example.com', :confirm => 'Are you sure?',
- # :method => "delete", :remote => true) %>
+ # <%= button_to('Destroy', 'http://www.example.com',
+ # :method => "delete", :remote => true, :data => { :confirm' => 'Are you sure?', :disable_with => 'loading...' }) %>
# # => "<form class='button_to' method='post' action='http://www.example.com' data-remote='true'>
# # <div>
# # <input name='_method' value='delete' type='hidden' />
- # # <input value='Destroy' type='submit' data-confirm='Are you sure?' />
+ # # <input value='Destroy' type='submit' data-disable-with='loading...' data-confirm='Are you sure?' />
# # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
# # </div>
# # </form>"
@@ -627,12 +643,24 @@ module ActionView
html_options = html_options.stringify_keys
html_options['data-remote'] = 'true' if link_to_remote_options?(options) || link_to_remote_options?(html_options)
+ disable_with = html_options.delete("disable_with")
confirm = html_options.delete('confirm')
method = html_options.delete('method')
- html_options["data-confirm"] = confirm if confirm
+ if confirm
+ ActiveSupport::Deprecation.warn ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead"
+
+ html_options["data-confirm"] = confirm
+ end
+
add_method_to_attributes!(html_options, method) if method
+ if disable_with
+ ActiveSupport::Deprecation.warn ":disable_with option is deprecated and will be removed from Rails 4.1. Use ':data => { :disable_with => \'Text\' }' instead"
+
+ html_options["data-disable-with"] = disable_with
+ end
+
html_options
else
link_to_remote_options?(options) ? {'data-remote' => 'true'} : {}
diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb
index 00989ec405..47dd932c71 100644
--- a/actionpack/lib/action_view/lookup_context.rb
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -96,7 +96,7 @@ module ActionView
# Helpers related to template lookup using the lookup context information.
module ViewPaths
- attr_reader :view_paths
+ attr_reader :view_paths, :html_fallback_for_js
# Whenever setting view paths, makes a copy so we can manipulate then in
# instance objects as we wish.
@@ -184,7 +184,10 @@ module ActionView
def formats=(values)
if values
values.concat(default_formats) if values.delete "*/*"
- values << :html if values == [:js]
+ if values == [:js]
+ values << :html
+ @html_fallback_for_js = true
+ end
end
super(values)
end
diff --git a/actionpack/lib/action_view/renderer/abstract_renderer.rb b/actionpack/lib/action_view/renderer/abstract_renderer.rb
index e3d8e9d508..6fb8cbb46c 100644
--- a/actionpack/lib/action_view/renderer/abstract_renderer.rb
+++ b/actionpack/lib/action_view/renderer/abstract_renderer.rb
@@ -22,5 +22,11 @@ module ActionView
def instrument(name, options={})
ActiveSupport::Notifications.instrument("render_#{name}.action_view", options){ yield }
end
+
+ def prepend_formats(formats)
+ formats = Array(formats)
+ return if formats.empty? || @lookup_context.html_fallback_for_js
+ @lookup_context.formats = formats | @lookup_context.formats
+ end
end
end
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
index 9100545718..a08a566b35 100644
--- a/actionpack/lib/action_view/renderer/partial_renderer.rb
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -320,6 +320,8 @@ module ActionView
@block = block
@details = extract_details(options)
+ prepend_formats(options[:formats])
+
if String === partial
@object = options[:object]
@path = partial
diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb
index 3c1b11396a..156ad4e547 100644
--- a/actionpack/lib/action_view/renderer/template_renderer.rb
+++ b/actionpack/lib/action_view/renderer/template_renderer.rb
@@ -8,9 +8,10 @@ module ActionView
template = determine_template(options)
context = @lookup_context
+ prepend_formats(template.formats)
+
unless context.rendered_format
- context.formats = template.formats unless template.formats.empty?
- context.rendered_format = context.formats.first
+ context.rendered_format = template.formats.first || formats.last
end
render_template(template, options[:layout], options[:locals])
diff --git a/actionpack/test/abstract/translation_test.rb b/actionpack/test/abstract/translation_test.rb
index 0194ee943f..99064a8b87 100644
--- a/actionpack/test/abstract/translation_test.rb
+++ b/actionpack/test/abstract/translation_test.rb
@@ -23,4 +23,17 @@ class TranslationControllerTest < ActiveSupport::TestCase
def test_action_controller_base_responds_to_l
assert_respond_to @controller, :l
end
+
+ def test_lazy_lookup
+ expected = 'bar'
+ @controller.stubs(:action_name => :index)
+ I18n.stubs(:translate).with('action_controller.base.index.foo').returns(expected)
+ assert_equal expected, @controller.t('.foo')
+ end
+
+ def test_default_translation
+ key, expected = 'one.two' 'bar'
+ I18n.stubs(:translate).with(key).returns(expected)
+ assert_equal expected, @controller.t(key)
+ end
end
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 37deb9c98a..8c7f6474e5 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -171,7 +171,7 @@ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
middleware.use "ActionDispatch::ParamsParser"
middleware.use "ActionDispatch::Cookies"
middleware.use "ActionDispatch::Flash"
- middleware.use "ActionDispatch::Head"
+ middleware.use "Rack::Head"
yield(middleware) if block_given?
end
end
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index b9cb93f0f4..afc00a3c9d 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -505,6 +505,10 @@ class FilterTest < ActionController::TestCase
def show
render :text => 'hello world'
end
+
+ def error
+ raise StandardError.new
+ end
end
class ImplicitActionsController < ActionController::Base
@@ -534,6 +538,13 @@ class FilterTest < ActionController::TestCase
assert_equal 'hello world', response.body
end
+ def test_sweeper_should_clean_up_if_exception_is_raised
+ assert_raise StandardError do
+ test_process(SweeperTestController, 'error')
+ end
+ assert_nil AppSweeper.instance.controller
+ end
+
def test_before_method_of_sweeper_should_always_return_true
sweeper = ActionController::Caching::Sweeper.send(:new)
assert sweeper.before(TestController.new)
diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb
index e4b34125ad..8340aab4d2 100644
--- a/actionpack/test/controller/flash_test.rb
+++ b/actionpack/test/controller/flash_test.rb
@@ -90,6 +90,10 @@ class FlashTest < ActionController::TestCase
def redirect_with_other_flashes
redirect_to '/wonderland', :flash => { :joyride => "Horses!" }
end
+
+ def redirect_with_foo_flash
+ redirect_to "/wonderland", :foo => 'for great justice'
+ end
end
tests TestController
@@ -203,6 +207,12 @@ class FlashTest < ActionController::TestCase
get :redirect_with_other_flashes
assert_equal "Horses!", @controller.send(:flash)[:joyride]
end
+
+ def test_redirect_to_with_adding_flash_types
+ @controller.class.add_flash_types :foo
+ get :redirect_with_foo_flash
+ assert_equal "for great justice", @controller.send(:flash)[:foo]
+ end
end
class FlashIntegrationTest < ActionDispatch::IntegrationTest
@@ -210,9 +220,7 @@ class FlashIntegrationTest < ActionDispatch::IntegrationTest
SessionSecret = 'b3c631c314c0bbca50c1b2843150fe33'
class TestController < ActionController::Base
- def dont_set_flash
- head :ok
- end
+ add_flash_types :bar
def set_flash
flash["that"] = "hello"
@@ -227,6 +235,11 @@ class FlashIntegrationTest < ActionDispatch::IntegrationTest
def use_flash
render :inline => "flash: #{flash["that"]}"
end
+
+ def set_bar
+ flash[:bar] = "for great justice"
+ head :ok
+ end
end
def test_flash
@@ -266,6 +279,14 @@ class FlashIntegrationTest < ActionDispatch::IntegrationTest
end
end
+ def test_added_flash_types_method
+ with_test_route_set do
+ get '/set_bar'
+ assert_response :success
+ assert_equal 'for great justice', @controller.bar
+ end
+ end
+
private
# Overwrite get to send SessionSecret in env hash
diff --git a/actionpack/test/controller/http_token_authentication_test.rb b/actionpack/test/controller/http_token_authentication_test.rb
index 3054c1684c..ad4e743be8 100644
--- a/actionpack/test/controller/http_token_authentication_test.rb
+++ b/actionpack/test/controller/http_token_authentication_test.rb
@@ -79,6 +79,14 @@ class HttpTokenAuthenticationTest < ActionController::TestCase
end
end
+ test "authentication request with badly formatted header" do
+ @request.env['HTTP_AUTHORIZATION'] = "Token foobar"
+ get :index
+
+ assert_response :unauthorized
+ assert_equal "HTTP Token: Access denied.\n", @response.body, "Authentication header was not properly parsed"
+ end
+
test "authentication request without credential" do
get :display
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index bdcd5561a8..c8e036b116 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -505,7 +505,7 @@ class RespondToControllerTest < ActionController::TestCase
end
class RespondWithController < ActionController::Base
- respond_to :html, :json
+ respond_to :html, :json, :touch
respond_to :xml, :except => :using_resource_with_block
respond_to :js, :only => [ :using_resource_with_block, :using_resource, 'using_hash_resource' ]
@@ -623,12 +623,14 @@ class RespondWithControllerTest < ActionController::TestCase
super
@request.host = "www.example.com"
Mime::Type.register_alias('text/html', :iphone)
+ Mime::Type.register_alias('text/html', :touch)
Mime::Type.register('text/x-mobile', :mobile)
end
def teardown
super
Mime::Type.unregister(:iphone)
+ Mime::Type.unregister(:touch)
Mime::Type.unregister(:mobile)
end
diff --git a/actionpack/test/controller/new_base/render_action_test.rb b/actionpack/test/controller/new_base/render_action_test.rb
index aa44e0b282..475bf9d3c9 100644
--- a/actionpack/test/controller/new_base/render_action_test.rb
+++ b/actionpack/test/controller/new_base/render_action_test.rb
@@ -86,8 +86,6 @@ module RenderAction
def setup
end
- describe "Both <controller_path>.html.erb and application.html.erb are missing"
-
test "rendering with layout => true" do
assert_raise(ArgumentError) do
get "/render_action/basic/hello_world_with_layout", {}, "action_dispatch.show_exceptions" => false
@@ -154,8 +152,6 @@ module RenderActionWithApplicationLayout
end
class LayoutTest < Rack::TestCase
- describe "Only application.html.erb is present and <controller_path>.html.erb is missing"
-
test "rendering implicit application.html.erb as layout" do
get "/render_action_with_application_layout/basic/hello_world"
@@ -232,8 +228,6 @@ module RenderActionWithControllerLayout
end
class ControllerLayoutTest < Rack::TestCase
- describe "Only <controller_path>.html.erb is present and application.html.erb is missing"
-
test "render hello_world and implicitly use <controller_path>.html.erb as a layout." do
get "/render_action_with_controller_layout/basic/hello_world"
@@ -290,8 +284,6 @@ module RenderActionWithBothLayouts
end
class ControllerLayoutTest < Rack::TestCase
- describe "Both <controller_path>.html.erb and application.html.erb are present"
-
test "rendering implicitly use <controller_path>.html.erb over application.html.erb as a layout" do
get "/render_action_with_both_layouts/basic/hello_world"
diff --git a/actionpack/test/controller/new_base/render_template_test.rb b/actionpack/test/controller/new_base/render_template_test.rb
index 00c7df2af8..156d87c321 100644
--- a/actionpack/test/controller/new_base/render_template_test.rb
+++ b/actionpack/test/controller/new_base/render_template_test.rb
@@ -160,8 +160,6 @@ module RenderTemplate
end
class TestWithLayout < Rack::TestCase
- describe "Rendering with :template using implicit or explicit layout"
-
test "rendering with implicit layout" do
with_routing do |set|
set.draw { get ':controller', :action => :index }
diff --git a/actionpack/test/controller/new_base/render_text_test.rb b/actionpack/test/controller/new_base/render_text_test.rb
index f8d02e8b6c..d6c3926a4d 100644
--- a/actionpack/test/controller/new_base/render_text_test.rb
+++ b/actionpack/test/controller/new_base/render_text_test.rb
@@ -63,8 +63,6 @@ module RenderText
end
class RenderTextTest < Rack::TestCase
- describe "Rendering text using render :text"
-
test "rendering text from an action with default options renders the text with the layout" do
with_routing do |set|
set.draw { get ':controller', :action => 'index' }
diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb
index 49137946fe..8990fc34d6 100644
--- a/actionpack/test/controller/test_case_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -197,6 +197,11 @@ XML
assert_raise(NoMethodError) { head :test_params, "document body", :id => 10 }
end
+ def test_options
+ options :test_params
+ assert_equal 200, @response.status
+ end
+
def test_process_without_flash
process :set_flash
assert_equal '><', flash['test']
@@ -635,7 +640,7 @@ XML
get :test_params, :path => ['hello', 'world']
assert_equal ['hello', 'world'], @request.path_parameters['path']
- assert_equal 'hello/world', @request.path_parameters['path'].to_s
+ assert_equal 'hello/world', @request.path_parameters['path'].to_param
end
end
@@ -913,4 +918,4 @@ class AnonymousControllerTest < ActionController::TestCase
get :index
assert_equal 'anonymous', @response.body
end
-end \ No newline at end of file
+end
diff --git a/actionpack/test/dispatch/mapper_test.rb b/actionpack/test/dispatch/mapper_test.rb
index 8070bdec8a..58457b0c28 100644
--- a/actionpack/test/dispatch/mapper_test.rb
+++ b/actionpack/test/dispatch/mapper_test.rb
@@ -98,6 +98,15 @@ module ActionDispatch
mapper.get '/*path', :to => 'pages#show', :format => true
assert_equal '/*path.:format', fakeset.conditions.first[:path_info]
end
+
+ def test_raising_helpful_error_on_invalid_arguments
+ fakeset = FakeSet.new
+ mapper = Mapper.new fakeset
+ app = lambda { |env| [200, {}, [""]] }
+ assert_raises ArgumentError do
+ mapper.mount app
+ end
+ end
end
end
end
diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb
index 9d77c3acc5..ed012093a7 100644
--- a/actionpack/test/dispatch/mime_type_test.rb
+++ b/actionpack/test/dispatch/mime_type_test.rb
@@ -118,6 +118,20 @@ class MimeTypeTest < ActiveSupport::TestCase
end
end
+ test "register callbacks" do
+ begin
+ registered_mimes = []
+ Mime::Type.register_callback do |mime|
+ registered_mimes << mime
+ end
+
+ Mime::Type.register("text/foo", :foo)
+ assert_equal registered_mimes, [Mime::FOO]
+ ensure
+ Mime::Type.unregister(:FOO)
+ end
+ end
+
test "custom type with extension aliases" do
begin
Mime::Type.register "text/foobar", :foobar, [], [:foo, "bar"]
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index 54fc1b208d..a434e49dbd 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -461,14 +461,6 @@ class RequestTest < ActiveSupport::TestCase
end
end
- test "head masquerading as get" do
- request = stub_request 'REQUEST_METHOD' => 'GET', "rack.methodoverride.original_method" => "HEAD"
- assert_equal "HEAD", request.method
- assert_equal "GET", request.request_method
- assert request.get?
- assert request.head?
- end
-
test "post masquerading as patch" do
request = stub_request 'REQUEST_METHOD' => 'PATCH', "rack.methodoverride.original_method" => "POST"
assert_equal "POST", request.method
diff --git a/actionpack/test/dispatch/routing/inspector_test.rb b/actionpack/test/dispatch/routing/inspector_test.rb
new file mode 100644
index 0000000000..97fc4f3e4b
--- /dev/null
+++ b/actionpack/test/dispatch/routing/inspector_test.rb
@@ -0,0 +1,170 @@
+require 'minitest/autorun'
+require 'action_controller'
+require 'rails/engine'
+require 'action_dispatch/routing/inspector'
+
+module ActionDispatch
+ module Routing
+ class RoutesInspectorTest < ActiveSupport::TestCase
+ def setup
+ @set = ActionDispatch::Routing::RouteSet.new
+ @inspector = ActionDispatch::Routing::RoutesInspector.new
+ app = ActiveSupport::OrderedOptions.new
+ app.config = ActiveSupport::OrderedOptions.new
+ app.config.assets = ActiveSupport::OrderedOptions.new
+ app.config.assets.prefix = '/sprockets'
+ Rails.stubs(:application).returns(app)
+ Rails.stubs(:env).returns("development")
+ end
+
+ def draw(&block)
+ @set.draw(&block)
+ @inspector.format(@set.routes)
+ end
+
+ def test_displaying_routes_for_engines
+ engine = Class.new(Rails::Engine) do
+ def self.to_s
+ "Blog::Engine"
+ end
+ end
+ engine.routes.draw do
+ get '/cart', :to => 'cart#show'
+ end
+
+ output = draw do
+ get '/custom/assets', :to => 'custom_assets#show'
+ mount engine => "/blog", :as => "blog"
+ end
+
+ expected = [
+ "custom_assets GET /custom/assets(.:format) custom_assets#show",
+ " blog /blog Blog::Engine",
+ "\nRoutes for Blog::Engine:",
+ "cart GET /cart(.:format) cart#show"
+ ]
+ assert_equal expected, output
+ end
+
+ def test_cart_inspect
+ output = draw do
+ get '/cart', :to => 'cart#show'
+ end
+ assert_equal ["cart GET /cart(.:format) cart#show"], output
+ end
+
+ def test_inspect_shows_custom_assets
+ output = draw do
+ get '/custom/assets', :to => 'custom_assets#show'
+ end
+ assert_equal ["custom_assets GET /custom/assets(.:format) custom_assets#show"], output
+ end
+
+ def test_inspect_routes_shows_resources_route
+ output = draw do
+ resources :articles
+ end
+ expected = [
+ " articles GET /articles(.:format) articles#index",
+ " POST /articles(.:format) articles#create",
+ " new_article GET /articles/new(.:format) articles#new",
+ "edit_article GET /articles/:id/edit(.:format) articles#edit",
+ " article GET /articles/:id(.:format) articles#show",
+ " PATCH /articles/:id(.:format) articles#update",
+ " PUT /articles/:id(.:format) articles#update",
+ " DELETE /articles/:id(.:format) articles#destroy" ]
+ assert_equal expected, output
+ end
+
+ def test_inspect_routes_shows_root_route
+ output = draw do
+ root :to => 'pages#main'
+ end
+ assert_equal ["root GET / pages#main"], output
+ end
+
+ def test_inspect_routes_shows_dynamic_action_route
+ output = draw do
+ get 'api/:action' => 'api'
+ end
+ assert_equal [" GET /api/:action(.:format) api#:action"], output
+ end
+
+ def test_inspect_routes_shows_controller_and_action_only_route
+ output = draw do
+ get ':controller/:action'
+ end
+ assert_equal [" GET /:controller/:action(.:format) :controller#:action"], output
+ end
+
+ def test_inspect_routes_shows_controller_and_action_route_with_constraints
+ output = draw do
+ get ':controller(/:action(/:id))', :id => /\d+/
+ end
+ assert_equal [" GET /:controller(/:action(/:id))(.:format) :controller#:action {:id=>/\\d+/}"], output
+ end
+
+ def test_rake_routes_shows_route_with_defaults
+ output = draw do
+ get 'photos/:id' => 'photos#show', :defaults => {:format => 'jpg'}
+ end
+ assert_equal [%Q[ GET /photos/:id(.:format) photos#show {:format=>"jpg"}]], output
+ end
+
+ def test_rake_routes_shows_route_with_constraints
+ output = draw do
+ get 'photos/:id' => 'photos#show', :id => /[A-Z]\d{5}/
+ end
+ assert_equal [" GET /photos/:id(.:format) photos#show {:id=>/[A-Z]\\d{5}/}"], output
+ end
+
+ class RackApp
+ def self.call(env)
+ end
+ end
+
+ def test_rake_routes_shows_route_with_rack_app
+ output = draw do
+ get 'foo/:id' => RackApp, :id => /[A-Z]\d{5}/
+ end
+ assert_equal [" GET /foo/:id(.:format) #{RackApp.name} {:id=>/[A-Z]\\d{5}/}"], output
+ end
+
+ def test_rake_routes_shows_route_with_rack_app_nested_with_dynamic_constraints
+ constraint = Class.new do
+ def to_s
+ "( my custom constraint )"
+ end
+ end
+
+ output = draw do
+ scope :constraint => constraint.new do
+ mount RackApp => '/foo'
+ end
+ end
+
+ assert_equal [" /foo #{RackApp.name} {:constraint=>( my custom constraint )}"], output
+ end
+
+ def test_rake_routes_dont_show_app_mounted_in_assets_prefix
+ output = draw do
+ get '/sprockets' => RackApp
+ end
+ assert_no_match(/RackApp/, output.first)
+ assert_no_match(/\/sprockets/, output.first)
+ end
+
+ def test_redirect
+ output = draw do
+ get "/foo" => redirect("/foo/bar"), :constraints => { :subdomain => "admin" }
+ get "/bar" => redirect(path: "/foo/bar", status: 307)
+ get "/foobar" => redirect{ "/foo/bar" }
+ end
+
+ assert_equal " foo GET /foo(.:format) redirect(301, /foo/bar) {:subdomain=>\"admin\"}", output[0]
+ assert_equal " bar GET /bar(.:format) redirect(307, path: /foo/bar)", output[1]
+ assert_equal "foobar GET /foobar(.:format) redirect(301)", output[2]
+ end
+ end
+ end
+end
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index fed26d89f8..205238990e 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -482,11 +482,17 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
get :preview, :on => :member
end
- resources :profiles, :param => :username do
+ resources :profiles, :param => :username, :username => /[a-z]+/ do
get :details, :on => :member
resources :messages
end
+ resources :orders do
+ constraints :download => /[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}/ do
+ resources :downloads, :param => :download, :shallow => true
+ end
+ end
+
scope :as => "routes" do
get "/c/:id", :as => :collision, :to => "collision#show"
get "/collision", :to => "collision#show"
@@ -2243,6 +2249,23 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal '34', @request.params[:id]
end
+ def test_custom_param_constraint
+ get '/profiles/bob1'
+ assert_equal 404, @response.status
+
+ get '/profiles/bob1/details'
+ assert_equal 404, @response.status
+
+ get '/profiles/bob1/messages/34'
+ assert_equal 404, @response.status
+ end
+
+ def test_shallow_custom_param
+ get '/downloads/0c0c0b68-d24b-11e1-a861-001ff3fffe6f.zip'
+ assert_equal 'downloads#show', @response.body
+ assert_equal '0c0c0b68-d24b-11e1-a861-001ff3fffe6f', @request.params[:download]
+ end
+
private
def with_https
old_https = https?
@@ -2679,3 +2702,30 @@ class TestInvalidUrls < ActionDispatch::IntegrationTest
end
end
end
+
+class TestOptionalRootSegments < ActionDispatch::IntegrationTest
+ stub_controllers do |routes|
+ Routes = routes
+ Routes.draw do
+ get '/(page/:page)', :to => 'pages#index', :as => :root
+ end
+ end
+
+ def app
+ Routes
+ end
+
+ include Routes.url_helpers
+
+ def test_optional_root_segments
+ get '/'
+ assert_equal 'pages#index', @response.body
+ assert_equal '/', root_path
+
+ get '/page/1'
+ assert_equal 'pages#index', @response.body
+ assert_equal '1', @request.params[:page]
+ assert_equal '/page/1', root_path('1')
+ assert_equal '/page/1', root_path(:page => '1')
+ end
+end
diff --git a/actionpack/test/fixtures/project.rb b/actionpack/test/fixtures/project.rb
index 2b53d39ed5..c124a9e605 100644
--- a/actionpack/test/fixtures/project.rb
+++ b/actionpack/test/fixtures/project.rb
@@ -1,3 +1,3 @@
class Project < ActiveRecord::Base
- has_and_belongs_to_many :developers, :uniq => true
+ has_and_belongs_to_many :developers, -> { uniq }
end
diff --git a/actionpack/test/fixtures/reply.rb b/actionpack/test/fixtures/reply.rb
index 0d3b0a7c98..16b53be18a 100644
--- a/actionpack/test/fixtures/reply.rb
+++ b/actionpack/test/fixtures/reply.rb
@@ -1,6 +1,6 @@
class Reply < ActiveRecord::Base
scope :base, -> { scoped }
- belongs_to :topic, :include => [:replies]
+ belongs_to :topic, -> { includes(:replies) }
belongs_to :developer
validates_presence_of :content
diff --git a/actionpack/test/fixtures/test/_changing_priority.html.erb b/actionpack/test/fixtures/test/_changing_priority.html.erb
new file mode 100644
index 0000000000..3225efc49a
--- /dev/null
+++ b/actionpack/test/fixtures/test/_changing_priority.html.erb
@@ -0,0 +1 @@
+HTML \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/_changing_priority.json.erb b/actionpack/test/fixtures/test/_changing_priority.json.erb
new file mode 100644
index 0000000000..7fa41dce66
--- /dev/null
+++ b/actionpack/test/fixtures/test/_changing_priority.json.erb
@@ -0,0 +1 @@
+JSON \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/_first_json_partial.json.erb b/actionpack/test/fixtures/test/_first_json_partial.json.erb
new file mode 100644
index 0000000000..790ee896db
--- /dev/null
+++ b/actionpack/test/fixtures/test/_first_json_partial.json.erb
@@ -0,0 +1 @@
+<%= render :partial => "test/second_json_partial" %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/_json_change_priority.json.erb b/actionpack/test/fixtures/test/_json_change_priority.json.erb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/actionpack/test/fixtures/test/_json_change_priority.json.erb
diff --git a/actionpack/test/fixtures/test/_second_json_partial.json.erb b/actionpack/test/fixtures/test/_second_json_partial.json.erb
new file mode 100644
index 0000000000..5ebb7f1afd
--- /dev/null
+++ b/actionpack/test/fixtures/test/_second_json_partial.json.erb
@@ -0,0 +1 @@
+Third level \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/change_priorty.html.erb b/actionpack/test/fixtures/test/change_priorty.html.erb
new file mode 100644
index 0000000000..5618977d05
--- /dev/null
+++ b/actionpack/test/fixtures/test/change_priorty.html.erb
@@ -0,0 +1,2 @@
+<%= render :partial => "test/json_change_priority", formats: :json %>
+HTML Template, but <%= render :partial => "test/changing_priority" %> partial \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/html_template.html.erb b/actionpack/test/fixtures/test/html_template.html.erb
new file mode 100644
index 0000000000..1bbc2b7f09
--- /dev/null
+++ b/actionpack/test/fixtures/test/html_template.html.erb
@@ -0,0 +1 @@
+<%= render :partial => "test/first_json_partial", formats: :json %> \ No newline at end of file
diff --git a/actionpack/test/template/active_model_helper_test.rb b/actionpack/test/template/active_model_helper_test.rb
index 24511df444..86bccdfade 100644
--- a/actionpack/test/template/active_model_helper_test.rb
+++ b/actionpack/test/template/active_model_helper_test.rb
@@ -41,6 +41,19 @@ class ActiveModelHelperTest < ActionView::TestCase
)
end
+ def test_select_with_errors
+ assert_dom_equal(
+ %(<div class="field_with_errors"><select name="post[author_name]" id="post_author_name"><option value="a">a</option>\n<option value="b">b</option></select></div>),
+ select("post", "author_name", [:a, :b])
+ )
+ end
+
+ def test_select_with_errors_and_blank_option
+ expected_dom = %(<div class="field_with_errors"><select name="post[author_name]" id="post_author_name"><option value="">Choose one...</option>\n<option value="a">a</option>\n<option value="b">b</option></select></div>)
+ assert_dom_equal(expected_dom, select("post", "author_name", [:a, :b], :include_blank => 'Choose one...'))
+ assert_dom_equal(expected_dom, select("post", "author_name", [:a, :b], :prompt => 'Choose one...'))
+ end
+
def test_date_select_with_errors
assert_dom_equal(
%(<div class="field_with_errors"><select id="post_updated_at_1i" name="post[updated_at(1i)]">\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n</select>\n<input id="post_updated_at_2i" name="post[updated_at(2i)]" type="hidden" value="6" />\n<input id="post_updated_at_3i" name="post[updated_at(3i)]" type="hidden" value="1" />\n</div>),
@@ -82,4 +95,5 @@ class ActiveModelHelperTest < ActionView::TestCase
ensure
ActionView::Base.field_error_proc = old_proc if old_proc
end
+
end
diff --git a/actionpack/test/template/capture_helper_test.rb b/actionpack/test/template/capture_helper_test.rb
index 17469f8c3e..234ac3252d 100644
--- a/actionpack/test/template/capture_helper_test.rb
+++ b/actionpack/test/template/capture_helper_test.rb
@@ -56,7 +56,7 @@ class CaptureHelperTest < ActionView::TestCase
def test_content_for_with_multiple_calls_and_flush
assert ! content_for?(:title)
content_for :title, 'foo'
- content_for :title, 'bar', true
+ content_for :title, 'bar', flush: true
assert_equal 'bar', content_for(:title)
end
@@ -75,7 +75,7 @@ class CaptureHelperTest < ActionView::TestCase
content_for :title do
'foo'
end
- content_for :title, true do
+ content_for :title, flush: true do
'bar'
end
assert_equal 'bar', content_for(:title)
@@ -86,7 +86,7 @@ class CaptureHelperTest < ActionView::TestCase
content_for :title do
'foo'
end
- content_for :title, nil, true do
+ content_for :title, nil, flush: true do
'bar'
end
assert_equal 'bar', content_for(:title)
@@ -97,7 +97,7 @@ class CaptureHelperTest < ActionView::TestCase
content_for :title do
'foo'
end
- content_for :title, false do
+ content_for :title, flush: false do
'bar'
end
assert_equal 'foobar', content_for(:title)
@@ -117,11 +117,11 @@ class CaptureHelperTest < ActionView::TestCase
def test_content_for_with_whitespace_block_and_flush
assert ! content_for?(:title)
content_for :title, 'foo'
- content_for :title, true do
+ content_for :title, flush: true do
output_buffer << " \n "
nil
end
- content_for :title, 'bar', true
+ content_for :title, 'bar', flush: true
assert_equal 'bar', content_for(:title)
end
@@ -131,9 +131,9 @@ class CaptureHelperTest < ActionView::TestCase
assert_equal nil, content_for(:title) { output_buffer << 'bar'; nil }
assert_equal nil, content_for(:title) { output_buffer << " \n "; nil }
assert_equal 'foobar', content_for(:title)
- assert_equal nil, content_for(:title, 'foo', true)
- assert_equal nil, content_for(:title, true) { output_buffer << 'bar'; nil }
- assert_equal nil, content_for(:title, true) { output_buffer << " \n "; nil }
+ assert_equal nil, content_for(:title, 'foo', flush: true)
+ assert_equal nil, content_for(:title, flush: true) { output_buffer << 'bar'; nil }
+ assert_equal nil, content_for(:title, flush: true) { output_buffer << " \n "; nil }
assert_equal 'bar', content_for(:title)
end
diff --git a/actionpack/test/template/erb/tag_helper_test.rb b/actionpack/test/template/erb/tag_helper_test.rb
index 1724d6432d..84e328d8be 100644
--- a/actionpack/test/template/erb/tag_helper_test.rb
+++ b/actionpack/test/template/erb/tag_helper_test.rb
@@ -3,9 +3,6 @@ require "template/erb/helper"
module ERBTest
class TagHelperTest < BlockTestCase
-
- extend ActiveSupport::Testing::Declarative
-
test "percent equals works for content_tag and does not require parenthesis on method call" do
assert_equal "<div>Hello world</div>", render_content("content_tag :div", "Hello world")
end
diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb
index 2322fb0406..bfc73172eb 100644
--- a/actionpack/test/template/form_options_helper_test.rb
+++ b/actionpack/test/template/form_options_helper_test.rb
@@ -1130,6 +1130,20 @@ class FormOptionsHelperTest < ActionView::TestCase
)
end
+ def test_options_for_select_with_data_element
+ assert_dom_equal(
+ "<option value=\"&lt;Denmark&gt;\" data-test=\"bold\">&lt;Denmark&gt;</option>",
+ options_for_select([ [ "<Denmark>", { :data => { :test => 'bold' } } ] ])
+ )
+ end
+
+ def test_options_for_select_with_data_element_with_special_characters
+ assert_dom_equal(
+ "<option value=\"&lt;Denmark&gt;\" data-test=\"&lt;bold&gt;\">&lt;Denmark&gt;</option>",
+ options_for_select([ [ "<Denmark>", { :data => { :test => '<bold>' } } ] ])
+ )
+ end
+
def test_options_for_select_with_element_attributes_and_selection
assert_dom_equal(
"<option value=\"&lt;Denmark&gt;\">&lt;Denmark&gt;</option>\n<option value=\"USA\" class=\"bold\" selected=\"selected\">USA</option>\n<option value=\"Sweden\">Sweden</option>",
@@ -1144,6 +1158,13 @@ class FormOptionsHelperTest < ActionView::TestCase
)
end
+ def test_options_for_select_with_special_characters
+ assert_dom_equal(
+ "<option value=\"&lt;Denmark&gt;\" onclick=\"alert(&quot;&lt;code&gt;&quot;)\">&lt;Denmark&gt;</option>",
+ options_for_select([ [ "<Denmark>", { :onclick => %(alert("<code>")) } ] ])
+ )
+ end
+
def test_option_html_attributes_from_without_hash
assert_equal(
{},
@@ -1172,13 +1193,6 @@ class FormOptionsHelperTest < ActionView::TestCase
)
end
- def test_option_html_attributes_with_special_characters
- assert_equal(
- {:onclick => "alert(&quot;&lt;code&gt;&quot;)"},
- option_html_attributes([ 'foo', 'bar', { :onclick => %(alert("<code>")) } ])
- )
- end
-
def test_grouped_collection_select
@post = Post.new
@post.origin = 'dk'
diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb
index 5d19e3274d..9afa4a2927 100644
--- a/actionpack/test/template/form_tag_helper_test.rb
+++ b/actionpack/test/template/form_tag_helper_test.rb
@@ -375,17 +375,33 @@ class FormTagHelperTest < ActionView::TestCase
def test_submit_tag
assert_dom_equal(
%(<input name='commit' data-disable-with="Saving..." onclick="alert('hello!')" type="submit" value="Save" />),
- submit_tag("Save", 'data-disable-with' => "Saving...", :onclick => "alert('hello!')")
+ submit_tag("Save", :onclick => "alert('hello!')", :data => { :disable_with => "Saving..." })
+ )
+ end
+
+ def test_submit_tag_with_no_onclick_options
+ assert_dom_equal(
+ %(<input name='commit' data-disable-with="Saving..." type="submit" value="Save" />),
+ submit_tag("Save", :data => { :disable_with => "Saving..." })
)
end
def test_submit_tag_with_confirmation
assert_dom_equal(
%(<input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />),
- submit_tag("Save", :confirm => "Are you sure?")
+ submit_tag("Save", :data => { :confirm => "Are you sure?" })
)
end
+ def test_submit_tag_with_deprecated_confirmation
+ assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
+ assert_dom_equal(
+ %(<input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />),
+ submit_tag("Save", :confirm => "Are you sure?")
+ )
+ end
+ end
+
def test_button_tag
assert_dom_equal(
%(<button name="button" type="submit">Button</button>),
@@ -437,13 +453,39 @@ class FormTagHelperTest < ActionView::TestCase
assert_dom_equal('<button name="temptation" type="button"><strong>Do not press me</strong></button>', output)
end
+ def test_button_tag_with_confirmation
+ assert_dom_equal(
+ %(<button name="button" type="submit" data-confirm="Are you sure?">Save</button>),
+ button_tag("Save", :type => "submit", :data => { :confirm => "Are you sure?" })
+ )
+ end
+
+ def test_button_tag_with_deprecated_confirmation
+ assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
+ assert_dom_equal(
+ %(<button name="button" type="submit" data-confirm="Are you sure?">Save</button>),
+ button_tag("Save", :type => "submit", :confirm => "Are you sure?")
+ )
+ end
+ end
+
def test_image_submit_tag_with_confirmation
assert_dom_equal(
%(<input type="image" src="/images/save.gif" data-confirm="Are you sure?" />),
- image_submit_tag("save.gif", :confirm => "Are you sure?")
+ image_submit_tag("save.gif", :data => { :confirm => "Are you sure?" })
)
end
+ def test_image_submit_tag_with_deprecated_confirmation
+ assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
+ assert_dom_equal(
+ %(<input type="image" src="/images/save.gif" data-confirm="Are you sure?" />),
+ image_submit_tag("save.gif", :confirm => "Are you sure?")
+ )
+ end
+ end
+
+
def test_color_field_tag
expected = %{<input id="car" name="car" type="color" />}
assert_dom_equal(expected, color_field_tag("car"))
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index 88ed8664c2..3ce1d20bd9 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -54,6 +54,16 @@ module RenderTestCases
assert_equal "Hello world", @view.render(:template => "test/one", :formats => [:html])
end
+ def test_render_partial_implicitly_use_format_of_the_rendered_partial
+ @view.lookup_context.formats = [:html]
+ assert_equal "Third level", @view.render(:template => "test/html_template")
+ end
+
+ def test_render_partial_use_last_prepended_format_for_partials_with_the_same_names
+ @view.lookup_context.formats = [:html]
+ assert_equal "\nHTML Template, but JSON partial", @view.render(:template => "test/change_priorty")
+ end
+
def test_render_template_with_a_missing_partial_of_another_format
@view.lookup_context.formats = [:html]
assert_raise ActionView::Template::Error, "Missing partial /missing with {:locale=>[:en], :formats=>[:json], :handlers=>[:erb, :builder]}" do
diff --git a/actionpack/test/template/test_case_test.rb b/actionpack/test/template/test_case_test.rb
index c005f040eb..387aafebd4 100644
--- a/actionpack/test/template/test_case_test.rb
+++ b/actionpack/test/template/test_case_test.rb
@@ -68,14 +68,14 @@ module ActionView
assert_nil self.class.determine_default_helper_class("String")
end
- test "delegates notice to request.flash" do
- view.request.flash.expects(:notice).with("this message")
- view.notice("this message")
+ test "delegates notice to request.flash[:notice]" do
+ view.request.flash.expects(:[]).with(:notice)
+ view.notice
end
- test "delegates alert to request.flash" do
- view.request.flash.expects(:alert).with("this message")
- view.alert("this message")
+ test "delegates alert to request.flash[:alert]" do
+ view.request.flash.expects(:[]).with(:alert)
+ view.alert
end
test "uses controller lookup context" do
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index 62608a727f..cb6f378ecb 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -90,17 +90,35 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_button_to_with_javascript_confirm
assert_dom_equal(
"<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :confirm => "Are you sure?")
+ button_to("Hello", "http://www.example.com", :data => { :confirm => "Are you sure?" })
)
end
+ def test_button_to_with_deprecated_confirm
+ assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
+ assert_dom_equal(
+ "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
+ button_to("Hello", "http://www.example.com", :confirm => "Are you sure?")
+ )
+ end
+ end
+
def test_button_to_with_javascript_disable_with
assert_dom_equal(
"<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input data-disable-with=\"Greeting...\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", 'data-disable-with' => "Greeting...")
+ button_to("Hello", "http://www.example.com", :data => { :disable_with => "Greeting..." })
)
end
+ def test_button_to_with_javascript_deprecated_disable_with
+ assert_deprecated ":disable_with option is deprecated and will be removed from Rails 4.1. Use ':data => { :disable_with => \'Text\' }' instead" do
+ assert_dom_equal(
+ "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input data-disable-with=\"Greeting...\" type=\"submit\" value=\"Hello\" /></div></form>",
+ button_to("Hello", "http://www.example.com", :disable_with => "Greeting...")
+ )
+ end
+ end
+
def test_button_to_with_remote_and_form_options
assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"custom-class\" data-remote=\"true\" data-type=\"json\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com", :remote => true, :form => { :class => "custom-class", "data-type" => "json" } )
end
@@ -108,10 +126,35 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_button_to_with_remote_and_javascript_confirm
assert_dom_equal(
"<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :remote => true, :confirm => "Are you sure?")
+ button_to("Hello", "http://www.example.com", :remote => true, :data => { :confirm => "Are you sure?" })
+ )
+ end
+
+ def test_button_to_with_remote_and_javascript_with_deprecated_confirm
+ assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
+ assert_dom_equal(
+ "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
+ button_to("Hello", "http://www.example.com", :remote => true, :confirm => "Are you sure?")
+ )
+ end
+ end
+
+ def test_button_to_with_remote_and_javascript_disable_with
+ assert_dom_equal(
+ "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-disable-with=\"Greeting...\" type=\"submit\" value=\"Hello\" /></div></form>",
+ button_to("Hello", "http://www.example.com", :remote => true, :data => { :disable_with => "Greeting..." })
)
end
+ def test_button_to_with_remote_and_javascript_deprecated_disable_with
+ assert_deprecated ":disable_with option is deprecated and will be removed from Rails 4.1. Use ':data => { :disable_with => \'Text\' }' instead" do
+ assert_dom_equal(
+ "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-disable-with=\"Greeting...\" type=\"submit\" value=\"Hello\" /></div></form>",
+ button_to("Hello", "http://www.example.com", :remote => true, :disable_with => "Greeting...")
+ )
+ end
+ end
+
def test_button_to_with_remote_false
assert_dom_equal(
"<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>",
@@ -208,18 +251,39 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_link_tag_with_javascript_confirm
assert_dom_equal(
"<a href=\"http://www.example.com\" data-confirm=\"Are you sure?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :confirm => "Are you sure?")
+ link_to("Hello", "http://www.example.com", :data => { :confirm => "Are you sure?" })
)
assert_dom_equal(
"<a href=\"http://www.example.com\" data-confirm=\"You can't possibly be sure, can you?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :confirm => "You can't possibly be sure, can you?")
+ link_to("Hello", "http://www.example.com", :data => { :confirm => "You can't possibly be sure, can you?" })
)
assert_dom_equal(
"<a href=\"http://www.example.com\" data-confirm=\"You can't possibly be sure,\n can you?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :confirm => "You can't possibly be sure,\n can you?")
+ link_to("Hello", "http://www.example.com", :data => { :confirm => "You can't possibly be sure,\n can you?" })
)
end
+ def test_link_tag_with_deprecated_confirm
+ assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
+ assert_dom_equal(
+ "<a href=\"http://www.example.com\" data-confirm=\"Are you sure?\">Hello</a>",
+ link_to("Hello", "http://www.example.com", :confirm => "Are you sure?")
+ )
+ end
+ assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
+ assert_dom_equal(
+ "<a href=\"http://www.example.com\" data-confirm=\"You can't possibly be sure, can you?\">Hello</a>",
+ link_to("Hello", "http://www.example.com", :confirm => "You can't possibly be sure, can you?")
+ )
+ end
+ assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
+ assert_dom_equal(
+ "<a href=\"http://www.example.com\" data-confirm=\"You can't possibly be sure,\n can you?\">Hello</a>",
+ link_to("Hello", "http://www.example.com", :confirm => "You can't possibly be sure,\n can you?")
+ )
+ end
+ end
+
def test_link_to_with_remote
assert_dom_equal(
"<a href=\"http://www.example.com\" data-remote=\"true\">Hello</a>",
@@ -265,18 +329,37 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_link_tag_using_post_javascript_and_confirm
assert_dom_equal(
"<a href=\"http://www.example.com\" data-method=\"post\" rel=\"nofollow\" data-confirm=\"Are you serious?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :method => :post, :confirm => "Are you serious?")
+ link_to("Hello", "http://www.example.com", :method => :post, :data => { :confirm => "Are you serious?" })
)
end
+ def test_link_tag_using_post_javascript_and_with_deprecated_confirm
+ assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
+ assert_dom_equal(
+ "<a href=\"http://www.example.com\" data-method=\"post\" rel=\"nofollow\" data-confirm=\"Are you serious?\">Hello</a>",
+ link_to("Hello", "http://www.example.com", :method => :post, :confirm => "Are you serious?")
+ )
+ end
+ end
+
def test_link_tag_using_delete_javascript_and_href_and_confirm
assert_dom_equal(
"<a href='\#' rel=\"nofollow\" data-confirm=\"Are you serious?\" data-method=\"delete\">Destroy</a>",
- link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#', :confirm => "Are you serious?"),
+ link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#', :data => { :confirm => "Are you serious?" }),
"When specifying url, form should be generated with it, but not this.href"
)
end
+ def test_link_tag_using_delete_javascript_and_href_and_with_deprecated_confirm
+ assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
+ assert_dom_equal(
+ "<a href='\#' rel=\"nofollow\" data-confirm=\"Are you serious?\" data-method=\"delete\">Destroy</a>",
+ link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#', :confirm => "Are you serious?"),
+ "When specifying url, form should be generated with it, but not this.href"
+ )
+ end
+ end
+
def test_link_tag_with_block
assert_dom_equal '<a href="/"><span>Example site</span></a>',
link_to('/') { content_tag(:span, 'Example site') }
diff --git a/activemodel/lib/active_model/conversion.rb b/activemodel/lib/active_model/conversion.rb
index 89d87a8b6f..57b1bc2ada 100644
--- a/activemodel/lib/active_model/conversion.rb
+++ b/activemodel/lib/active_model/conversion.rb
@@ -45,18 +45,30 @@ module ActiveModel
# Returns an Enumerable of all key attributes if any is set, regardless if
# the object is persisted or not. If there no key attributes, returns +nil+.
+ #
+ # class Person < ActiveRecord::Base
+ # end
+ #
+ # person = Person.create
+ # person.to_key # => [1]
def to_key
key = respond_to?(:id) && id
key ? [key] : nil
end
- # Returns a string representing the object's key suitable for use in URLs,
- # or +nil+ if <tt>persisted?</tt> is false.
+ # Returns a +string+ representing the object's key suitable for use in URLs,
+ # or +nil+ if <tt>persisted?</tt> is +false+.
+ #
+ # class Person < ActiveRecord::Base
+ # end
+ #
+ # person = Person.create
+ # person.to_param # => "1"
def to_param
persisted? ? to_key.join('-') : nil
end
- # Returns a string identifying the path associated with the object.
+ # Returns a +string+ identifying the path associated with the object.
# ActionPack uses this to find a suitable partial to represent the object.
#
# class Person
diff --git a/activemodel/lib/active_model/mass_assignment_security.rb b/activemodel/lib/active_model/mass_assignment_security.rb
index 8f2c0bf00a..e57fcf1610 100644
--- a/activemodel/lib/active_model/mass_assignment_security.rb
+++ b/activemodel/lib/active_model/mass_assignment_security.rb
@@ -141,8 +141,10 @@ module ActiveModel
#
# attr_accessor :name, :credit_rating
#
- # attr_accessible :name
- # attr_accessible :name, :credit_rating, :as => :admin
+ # # Both admin and default user can change name of a customer
+ # attr_accessible :name, :as => [:admin, :default]
+ # # Only admin can change credit rating of a customer
+ # attr_accessible :credit_rating, :as => :admin
#
# def assign_attributes(values, options = {})
# sanitize_for_mass_assignment(values, options[:as]).each do |k, v|
diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb
index f5ea285ccb..976dcf23d8 100644
--- a/activemodel/lib/active_model/observing.rb
+++ b/activemodel/lib/active_model/observing.rb
@@ -215,7 +215,7 @@ module ActiveModel
# end
# end
def observed_classes
- Array(observed_class)
+ [observed_class].compact.flatten
end
# The class observed by default is inferred from the observer's class name:
@@ -238,8 +238,7 @@ module ActiveModel
# Send observed_method(object) if the method exists and
# the observer is enabled for the given object's class.
def update(observed_method, object, *extra_args, &block) #:nodoc:
- return unless respond_to?(observed_method)
- return if disabled_for?(object)
+ return if !respond_to?(observed_method) || disabled_for?(object)
send(observed_method, object, *extra_args, &block)
end
diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb
index 6d8fd21814..8a63014ffb 100644
--- a/activemodel/lib/active_model/serialization.rb
+++ b/activemodel/lib/active_model/serialization.rb
@@ -25,17 +25,16 @@ module ActiveModel
# person.name = "Bob"
# person.serializable_hash # => {"name"=>"Bob"}
#
- # You need to declare an attributes hash which contains the attributes
- # you want to serialize. Attributes must be strings, not symbols.
- # When called, serializable hash will use
- # instance methods that match the name of the attributes hash's keys.
- # In order to override this behavior, take a look at the private
- # method +read_attribute_for_serialization+.
+ # You need to declare an attributes hash which contains the attributes you
+ # want to serialize. Attributes must be strings, not symbols. When called,
+ # serializable hash will use instance methods that match the name of the
+ # attributes hash's keys. In order to override this behavior, take a look at
+ # the private method +read_attribute_for_serialization+.
#
# Most of the time though, you will want to include the JSON or XML
# serializations. Both of these modules automatically include the
- # <tt>ActiveModel::Serialization</tt> module, so there is no need to explicitly
- # include it.
+ # <tt>ActiveModel::Serialization</tt> module, so there is no need to
+ # explicitly include it.
#
# A minimal implementation including XML and JSON would be:
#
@@ -64,13 +63,37 @@ module ActiveModel
# person.to_json # => "{\"name\":\"Bob\"}"
# person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
#
- # Valid options are <tt>:only</tt>, <tt>:except</tt>, <tt>:methods</tt> and <tt>:include</tt>.
- # The following are all valid examples:
+ # Valid options are <tt>:only</tt>, <tt>:except</tt>, <tt>:methods</tt> and
+ # <tt>:include</tt>. The following are all valid examples:
#
- # person.serializable_hash(:only => 'name')
- # person.serializable_hash(:include => :address)
- # person.serializable_hash(:include => { :address => { :only => 'city' }})
+ # person.serializable_hash(only: 'name')
+ # person.serializable_hash(include: :address)
+ # person.serializable_hash(include: { address: { only: 'city' }})
module Serialization
+ # Returns a serialized hash of your object.
+ #
+ # class Person
+ # include ActiveModel::Serialization
+ #
+ # attr_accessor :name, :age
+ #
+ # def attributes
+ # {'name' => nil, 'age' => nil}
+ # end
+ #
+ # def capitalized_name
+ # name.capitalize
+ # end
+ # end
+ #
+ # person = Person.new
+ # person.name = 'bob'
+ # person.age = 22
+ # person.serializable_hash # => {"name"=>"bob", "age"=>22}
+ # person.serializable_hash(only: :name) # => {"name"=>"bob"}
+ # person.serializable_hash(except: :name) # => {"age"=>22}
+ # person.serializable_hash(methods: :capitalized_name)
+ # # => {"name"=>"bob", "age"=>22, "capitalized_name"=>"Bob"}
def serializable_hash(options = nil)
options ||= {}
@@ -115,7 +138,6 @@ module ActiveModel
# @data[key]
# end
# end
- #
alias :read_attribute_for_serialization :send
# Add associations specified via the <tt>:include</tt> option.
diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb
index b4baf3a946..e4c7553cb8 100644
--- a/activemodel/lib/active_model/serializers/json.rb
+++ b/activemodel/lib/active_model/serializers/json.rb
@@ -87,8 +87,12 @@ module ActiveModel
# # { "comments" => [ { "body" => "Don't think too hard" } ],
# # "title" => "So I was thinking" } ] }
def as_json(options = nil)
- root = include_root_in_json
- root = options[:root] if options.try(:key?, :root)
+ root = if options && options.key?(:root)
+ options[:root]
+ else
+ include_root_in_json
+ end
+
if root
root = self.class.model_name.element if root == true
{ root => serializable_hash(options) }
diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb
index 43651094cf..8d5ebf527f 100644
--- a/activemodel/lib/active_model/validations/acceptance.rb
+++ b/activemodel/lib/active_model/validations/acceptance.rb
@@ -27,7 +27,7 @@ module ActiveModel
#
# class Person < ActiveRecord::Base
# validates_acceptance_of :terms_of_service
- # validates_acceptance_of :eula, :message => "must be abided"
+ # validates_acceptance_of :eula, message: "must be abided"
# end
#
# If the database column does not exist, the +terms_of_service+ attribute
@@ -37,29 +37,17 @@ module ActiveModel
# Configuration options:
# * <tt>:message</tt> - A custom error message (default is: "must be
# accepted").
- # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
- # validation contexts by default (+nil+), other options are <tt>:create</tt>
- # and <tt>:update</tt>.
# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default
- # is true).
+ # is +true+).
# * <tt>:accept</tt> - Specifies value that is considered accepted.
# The default value is a string "1", which makes it easy to relate to
# an HTML checkbox. This should be set to +true+ if you are validating
# a database column, since the attribute is typecast from "1" to +true+
# before validation.
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
- # if the validation should occur (e.g. <tt>:if => :allow_validation</tt>,
- # or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
- # method, proc or string should return or evaluate to a true or false
- # value.
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to
- # determine if the validation should not occur (for example,
- # <tt>:unless => :skip_validation</tt>, or
- # <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).
- # The method, proc or string should return or evaluate to a true or
- # false value.
- # * <tt>:strict</tt> - Specifies whether validation should be strict.
- # See <tt>ActiveModel::Validation#validates!</tt> for more information.
+ #
+ # There is also a list of default options supported by every validator:
+ # +:if+, +:unless+, +:on+ and +:strict+.
+ # See <tt>ActiveModel::Validation#validates</tt> for more information
def validates_acceptance_of(*attr_names)
validates_with AcceptanceValidator, _merge_attributes(attr_names)
end
diff --git a/activemodel/lib/active_model/validations/clusivity.rb b/activemodel/lib/active_model/validations/clusivity.rb
index b632a2bd6b..643a6f2b7c 100644
--- a/activemodel/lib/active_model/validations/clusivity.rb
+++ b/activemodel/lib/active_model/validations/clusivity.rb
@@ -2,12 +2,12 @@ require 'active_support/core_ext/range.rb'
module ActiveModel
module Validations
- module Clusivity
+ module Clusivity #:nodoc:
ERROR_MESSAGE = "An object with the method #include? or a proc or lambda is required, " <<
- "and must be supplied as the :in option of the configuration hash"
+ "and must be supplied as the :in (or :within) option of the configuration hash"
def check_validity!
- unless [:include?, :call].any?{ |method| options[:in].respond_to?(method) }
+ unless [:include?, :call].any?{ |method| delimiter.respond_to?(method) }
raise ArgumentError, ERROR_MESSAGE
end
end
@@ -15,11 +15,14 @@ module ActiveModel
private
def include?(record, value)
- delimiter = options[:in]
exclusions = delimiter.respond_to?(:call) ? delimiter.call(record) : delimiter
exclusions.send(inclusion_method(exclusions), value)
end
+ def delimiter
+ @delimiter ||= options[:in] || options[:within]
+ end
+
# In Ruby 1.9 <tt>Range#include?</tt> on non-numeric ranges checks all possible values in the
# range for equality, so it may be slow for large ranges. The new <tt>Range#cover?</tt>
# uses the previous logic of comparing a value with the range endpoints.
diff --git a/activemodel/lib/active_model/validations/confirmation.rb b/activemodel/lib/active_model/validations/confirmation.rb
index b6cf82fb19..baa034eca6 100644
--- a/activemodel/lib/active_model/validations/confirmation.rb
+++ b/activemodel/lib/active_model/validations/confirmation.rb
@@ -25,7 +25,7 @@ module ActiveModel
# class Person < ActiveRecord::Base
# validates_confirmation_of :user_name, :password
# validates_confirmation_of :email_address,
- # :message => "should match confirmation"
+ # message: 'should match confirmation'
# end
#
# View:
@@ -38,29 +38,18 @@ module ActiveModel
# attribute.
#
# NOTE: This check is performed only if +password_confirmation+ is not
- # +nil+. To require confirmation, make sure
- # to add a presence check for the confirmation attribute:
+ # +nil+. To require confirmation, make sure to add a presence check for
+ # the confirmation attribute:
#
- # validates_presence_of :password_confirmation, :if => :password_changed?
+ # validates_presence_of :password_confirmation, if: :password_changed?
#
# Configuration options:
# * <tt>:message</tt> - A custom error message (default is: "doesn't match
# confirmation").
- # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
- # validation contexts by default (+nil+), other options are <tt>:create</tt>
- # and <tt>:update</tt>.
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
- # if the validation should occur (e.g. <tt>:if => :allow_validation</tt>,
- # or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
- # method, proc or string should return or evaluate to a true or false
- # value.
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to
- # determine if the validation should not occur (e.g.
- # <tt>:unless => :skip_validation</tt>, or
- # <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
- # method, proc or string should return or evaluate to a true or false value.
- # * <tt>:strict</tt> - Specifies whether validation should be strict.
- # See <tt>ActiveModel::Validation#validates!</tt> for more information.
+ #
+ # There is also a list of default options supported by every validator:
+ # +:if+, +:unless+, +:on+ and +:strict+.
+ # See <tt>ActiveModel::Validation#validates</tt> for more information
def validates_confirmation_of(*attr_names)
validates_with ConfirmationValidator, _merge_attributes(attr_names)
end
diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb
index c8d7057606..dc3368c569 100644
--- a/activemodel/lib/active_model/validations/exclusion.rb
+++ b/activemodel/lib/active_model/validations/exclusion.rb
@@ -9,7 +9,7 @@ module ActiveModel
def validate_each(record, attribute, value)
if include?(record, value)
- record.errors.add(attribute, :exclusion, options.except(:in).merge!(:value => value))
+ record.errors.add(attribute, :exclusion, options.except(:in, :within).merge!(:value => value))
end
end
end
@@ -19,37 +19,29 @@ module ActiveModel
# particular enumerable object.
#
# class Person < ActiveRecord::Base
- # validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here"
- # validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60"
- # validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension %{value} is not allowed"
- # validates_exclusion_of :password, :in => lambda { |p| [p.username, p.first_name] },
- # :message => "should not be the same as your username or first name"
+ # validates_exclusion_of :username, in: %w( admin superuser ), message: "You don't belong here"
+ # validates_exclusion_of :age, in: 30..60, message: 'This site is only for under 30 and over 60'
+ # validates_exclusion_of :format, in: %w( mov avi ), message: "extension %{value} is not allowed"
+ # validates_exclusion_of :password, in: ->(person) { [person.username, person.first_name] },
+ # message: 'should not be the same as your username or first name'
# end
#
# Configuration options:
- # * <tt>:in</tt> - An enumerable object of items that the value shouldn't be
- # part of. This can be supplied as a proc or lambda which returns an
+ # * <tt>:in</tt> - An enumerable object of items that the value shouldn't
+ # be part of. This can be supplied as a proc or lambda which returns an
# enumerable. If the enumerable is a range the test is performed with
+ # * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
# <tt>Range#cover?</tt>, otherwise with <tt>include?</tt>.
# * <tt>:message</tt> - Specifies a custom error message (default is: "is
# reserved").
- # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute
- # is +nil+ (default is +false+).
+ # * <tt>:allow_nil</tt> - If set to true, skips this validation if the
+ # attribute is +nil+ (default is +false+).
# * <tt>:allow_blank</tt> - If set to true, skips this validation if the
# attribute is blank(default is +false+).
- # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
- # validation contexts by default (+nil+), other options are <tt>:create</tt>
- # and <tt>:update</tt>.
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
- # the validation should occur (e.g. <tt>:if => :allow_validation</tt>, or
- # <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
- # or string should return or evaluate to a true or false value.
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
- # if the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
- # or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
- # method, proc or string should return or evaluate to a true or false value.
- # * <tt>:strict</tt> - Specifies whether validation should be strict.
- # See <tt>ActiveModel::Validation#validates!</tt> for more information.
+ #
+ # There is also a list of default options supported by every validator:
+ # +:if+, +:unless+, +:on+ and +:strict+.
+ # See <tt>ActiveModel::Validation#validates</tt> for more information
def validates_exclusion_of(*attr_names)
validates_with ExclusionValidator, _merge_attributes(attr_names)
end
diff --git a/activemodel/lib/active_model/validations/format.rb b/activemodel/lib/active_model/validations/format.rb
index d48987c482..80150229a0 100644
--- a/activemodel/lib/active_model/validations/format.rb
+++ b/activemodel/lib/active_model/validations/format.rb
@@ -57,14 +57,14 @@ module ActiveModel
# attribute matches the regular expression:
#
# class Person < ActiveRecord::Base
- # validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :on => :create
+ # validates_format_of :email, with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create
# end
#
# Alternatively, you can require that the specified attribute does _not_
# match the regular expression:
#
# class Person < ActiveRecord::Base
- # validates_format_of :email, :without => /NOSPAM/
+ # validates_format_of :email, without: /NOSPAM/
# end
#
# You can also provide a proc or lambda which will determine the regular
@@ -73,15 +73,16 @@ module ActiveModel
# class Person < ActiveRecord::Base
# # Admin can have number as a first letter in their screen name
# validates_format_of :screen_name,
- # :with => lambda{ |person| person.admin? ? /\A[a-z0-9][a-z0-9_\-]*\z/i : /\A[a-z][a-z0-9_\-]*\z/i }
+ # with: ->(person) { person.admin? ? /\A[a-z0-9][a-z0-9_\-]*\z/i : /\A[a-z][a-z0-9_\-]*\z/i }
# end
#
# Note: use <tt>\A</tt> and <tt>\Z</tt> to match the start and end of the
# string, <tt>^</tt> and <tt>$</tt> match the start/end of a line.
#
- # Due to frequent misuse of <tt>^</tt> and <tt>$</tt>, you need to pass the
- # :multiline => true option in case you use any of these two anchors in the provided
- # regular expression. In most cases, you should be using <tt>\A</tt> and <tt>\z</tt>.
+ # Due to frequent misuse of <tt>^</tt> and <tt>$</tt>, you need to pass
+ # the <tt>multiline: true</tt> option in case you use any of these two
+ # anchors in the provided regular expression. In most cases, you should be
+ # using <tt>\A</tt> and <tt>\z</tt>.
#
# You must pass either <tt>:with</tt> or <tt>:without</tt> as an option.
# In addition, both must be a regular expression or a proc or lambda, or
@@ -89,32 +90,24 @@ module ActiveModel
#
# Configuration options:
# * <tt>:message</tt> - A custom error message (default is: "is invalid").
- # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute
- # is +nil+ (default is +false+).
+ # * <tt>:allow_nil</tt> - If set to true, skips this validation if the
+ # attribute is +nil+ (default is +false+).
# * <tt>:allow_blank</tt> - If set to true, skips this validation if the
# attribute is blank (default is +false+).
# * <tt>:with</tt> - Regular expression that if the attribute matches will
- # result in a successful validation. This can be provided as a proc or lambda
- # returning regular expression which will be called at runtime.
- # * <tt>:without</tt> - Regular expression that if the attribute does not match
- # will result in a successful validation. This can be provided as a proc or
+ # result in a successful validation. This can be provided as a proc or
# lambda returning regular expression which will be called at runtime.
- # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
- # validation contexts by default (+nil+), other options are <tt>:create</tt>
- # and <tt>:update</tt>.
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
- # if the validation should occur (e.g. <tt>:if => :allow_validation</tt>, or
- # <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
- # or string should return or evaluate to a true or false value.
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
- # if the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
- # or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
- # method, proc or string should return or evaluate to a true or false value.
- # * <tt>:strict</tt> - Specifies whether validation should be strict.
- # See <tt>ActiveModel::Validation#validates!</tt> for more information.
+ # * <tt>:without</tt> - Regular expression that if the attribute does not
+ # match will result in a successful validation. This can be provided as
+ # a proc or lambda returning regular expression which will be called at
+ # runtime.
# * <tt>:multiline</tt> - Set to true if your regular expression contains
# anchors that match the beginning or end of lines as opposed to the
# beginning or end of the string. These anchors are <tt>^</tt> and <tt>$</tt>.
+ #
+ # There is also a list of default options supported by every validator:
+ # +:if+, +:unless+, +:on+ and +:strict+.
+ # See <tt>ActiveModel::Validation#validates</tt> for more information
def validates_format_of(*attr_names)
validates_with FormatValidator, _merge_attributes(attr_names)
end
diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb
index 154db5aedc..c2835c550b 100644
--- a/activemodel/lib/active_model/validations/inclusion.rb
+++ b/activemodel/lib/active_model/validations/inclusion.rb
@@ -9,7 +9,7 @@ module ActiveModel
def validate_each(record, attribute, value)
unless include?(record, value)
- record.errors.add(attribute, :inclusion, options.except(:in).merge!(:value => value))
+ record.errors.add(attribute, :inclusion, options.except(:in, :within).merge!(:value => value))
end
end
end
@@ -19,36 +19,28 @@ module ActiveModel
# particular enumerable object.
#
# class Person < ActiveRecord::Base
- # validates_inclusion_of :gender, :in => %w( m f )
- # validates_inclusion_of :age, :in => 0..99
- # validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension %{value} is not included in the list"
- # validates_inclusion_of :states, :in => lambda{ |person| STATES[person.country] }
+ # validates_inclusion_of :gender, in: %w( m f )
+ # validates_inclusion_of :age, in: 0..99
+ # validates_inclusion_of :format, in: %w( jpg gif png ), message: "extension %{value} is not included in the list"
+ # validates_inclusion_of :states, in: ->(person) { STATES[person.country] }
# end
#
# Configuration options:
# * <tt>:in</tt> - An enumerable object of available items. This can be
- # supplied as a proc or lambda which returns an enumerable. If the enumerable
- # is a range the test is performed with <tt>Range#cover?</tt>, otherwise with
- # <tt>include?</tt>.
- # * <tt>:message</tt> - Specifies a custom error message (default is: "is not
- # included in the list").
- # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute
- # is +nil+ (default is +false+).
- # * <tt>:allow_blank</tt> - If set to true, skips this validation if the
+ # supplied as a proc or lambda which returns an enumerable. If the
+ # enumerable is a range the test is performed with <tt>Range#cover?</tt>,
+ # otherwise with <tt>include?</tt>.
+ # * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
+ # * <tt>:message</tt> - Specifies a custom error message (default is: "is
+ # not included in the list").
+ # * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the
+ # attribute is +nil+ (default is +false+).
+ # * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the
# attribute is blank (default is +false+).
- # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
- # validation contexts by default (+nil+), other options are <tt>:create</tt>
- # and <tt>:update</tt>.
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
- # the validation should occur (e.g. <tt>:if => :allow_validation</tt>, or
- # <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
- # or string should return or evaluate to a true or false value.
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
- # if the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
- # or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
- # method, proc or string should return or evaluate to a true or false value.
- # * <tt>:strict</tt> - Specifies whether validation should be strict.
- # See <tt>ActiveModel::Validation#validates!</tt> for more information.
+ #
+ # There is also a list of default options supported by every validator:
+ # +:if+, +:unless+, +:on+ and +:strict+.
+ # See <tt>ActiveModel::Validation#validates</tt> for more information
def validates_inclusion_of(*attr_names)
validates_with InclusionValidator, _merge_attributes(attr_names)
end
diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb
index 40ebe0cd2e..aa72ea41c7 100644
--- a/activemodel/lib/active_model/validations/length.rb
+++ b/activemodel/lib/active_model/validations/length.rb
@@ -62,56 +62,48 @@ module ActiveModel
module HelperMethods
- # Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time:
+ # Validates that the specified attribute matches the length restrictions
+ # supplied. Only one option can be used at a time:
#
# class Person < ActiveRecord::Base
- # validates_length_of :first_name, :maximum => 30
- # validates_length_of :last_name, :maximum => 30, :message => "less than 30 if you don't mind"
- # validates_length_of :fax, :in => 7..32, :allow_nil => true
- # validates_length_of :phone, :in => 7..32, :allow_blank => true
- # validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name"
- # validates_length_of :zip_code, :minimum => 5, :too_short => "please enter at least 5 characters"
- # validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with 4 characters... don't play me."
- # validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least 100 words.",
- # :tokenizer => lambda { |str| str.scan(/\w+/) }
+ # validates_length_of :first_name, maximum: 30
+ # validates_length_of :last_name, maximum: 30, message: "less than 30 if you don't mind"
+ # validates_length_of :fax, in: 7..32, allow_nil: true
+ # validates_length_of :phone, in: 7..32, allow_blank: true
+ # validates_length_of :user_name, within: 6..20, too_long: 'pick a shorter name', too_short: 'pick a longer name'
+ # validates_length_of :zip_code, minimum: 5, too_short: 'please enter at least 5 characters'
+ # validates_length_of :smurf_leader, is: 4, message: "papa is spelled with 4 characters... don't play me."
+ # validates_length_of :essay, minimum: 100, too_short: 'Your essay must be at least 100 words.',
+ # tokenizer: ->(str) { str.scan(/\w+/) }
# end
#
# Configuration options:
# * <tt>:minimum</tt> - The minimum size of the attribute.
# * <tt>:maximum</tt> - The maximum size of the attribute.
# * <tt>:is</tt> - The exact size of the attribute.
- # * <tt>:within</tt> - A range specifying the minimum and maximum size of the
- # attribute.
- # * <tt>:in</tt> - A synonym(or alias) for <tt>:within</tt>.
+ # * <tt>:within</tt> - A range specifying the minimum and maximum size of
+ # the attribute.
+ # * <tt>:in</tt> - A synonym (or alias) for <tt>:within</tt>.
# * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation.
# * <tt>:allow_blank</tt> - Attribute may be blank; skip validation.
# * <tt>:too_long</tt> - The error message if the attribute goes over the
# maximum (default is: "is too long (maximum is %{count} characters)").
# * <tt>:too_short</tt> - The error message if the attribute goes under the
# minimum (default is: "is too short (min is %{count} characters)").
- # * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method
- # and the attribute is the wrong size (default is: "is the wrong length
- # (should be %{count} characters)").
+ # * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt>
+ # method and the attribute is the wrong size (default is: "is the wrong
+ # length (should be %{count} characters)").
# * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>,
# <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate
# <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
- # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
- # validation contexts by default (+nil+), other options are <tt>:create</tt>
- # and <tt>:update</tt>.
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
- # the validation should occur (e.g. <tt>:if => :allow_validation</tt>, or
- # <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
- # or string should return or evaluate to a true or false value.
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
- # if the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
- # or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
- # proc or string should return or evaluate to a true or false value.
# * <tt>:tokenizer</tt> - Specifies how to split up the attribute string.
- # (e.g. <tt>:tokenizer => lambda {|str| str.scan(/\w+/)}</tt> to count words
- # as in above example). Defaults to <tt>lambda{ |value| value.split(//) }</tt>
+ # (e.g. <tt>tokenizer: ->(str) { str.scan(/\w+/) }</tt> to count words
+ # as in above example). Defaults to <tt>->(value) { value.split(//) }</tt>
# which counts individual characters.
- # * <tt>:strict</tt> - Specifies whether validation should be strict.
- # See <tt>ActiveModel::Validation#validates!</tt> for more information.
+ #
+ # There is also a list of default options supported by every validator:
+ # +:if+, +:unless+, +:on+ and +:strict+.
+ # See <tt>ActiveModel::Validation#validates</tt> for more information
def validates_length_of(*attr_names)
validates_with LengthValidator, _merge_attributes(attr_names)
end
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index 1069ed3906..edebca94a8 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -79,48 +79,40 @@ module ActiveModel
end
module HelperMethods
- # Validates whether the value of the specified attribute is numeric by trying
- # to convert it to a float with Kernel.Float (if <tt>only_integer</tt> is false)
- # or applying it to the regular expression <tt>/\A[\+\-]?\d+\Z/</tt> (if
- # <tt>only_integer</tt> is set to true).
+ # Validates whether the value of the specified attribute is numeric by
+ # trying to convert it to a float with Kernel.Float (if <tt>only_integer</tt>
+ # is +false+) or applying it to the regular expression <tt>/\A[\+\-]?\d+\Z/</tt>
+ # (if <tt>only_integer</tt> is set to +true+).
#
# class Person < ActiveRecord::Base
- # validates_numericality_of :value, :on => :create
+ # validates_numericality_of :value, on: :create
# end
#
# Configuration options:
# * <tt>:message</tt> - A custom error message (default is: "is not a number").
- # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
- # validation contexts by default (+nil+), other options are <tt>:create</tt>
- # and <tt>:update</tt>.
- # * <tt>:only_integer</tt> - Specifies whether the value has to be an integer,
- # e.g. an integral value (default is +false+).
+ # * <tt>:only_integer</tt> - Specifies whether the value has to be an
+ # integer, e.g. an integral value (default is +false+).
# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is
# +false+). Notice that for fixnum and float columns empty strings are
# converted to +nil+.
# * <tt>:greater_than</tt> - Specifies the value must be greater than the
# supplied value.
- # * <tt>:greater_than_or_equal_to</tt> - Specifies the value must be greater
- # than or equal the supplied value.
- # * <tt>:equal_to</tt> - Specifies the value must be equal to the supplied value.
- # * <tt>:less_than</tt> - Specifies the value must be less than the supplied
- # value.
- # * <tt>:less_than_or_equal_to</tt> - Specifies the value must be less than or
- # equal the supplied value.
- # * <tt>:other_than</tt> - Specifies the value must be other than the supplied
+ # * <tt>:greater_than_or_equal_to</tt> - Specifies the value must be
+ # greater than or equal the supplied value.
+ # * <tt>:equal_to</tt> - Specifies the value must be equal to the supplied
# value.
+ # * <tt>:less_than</tt> - Specifies the value must be less than the
+ # supplied value.
+ # * <tt>:less_than_or_equal_to</tt> - Specifies the value must be less
+ # than or equal the supplied value.
+ # * <tt>:other_than</tt> - Specifies the value must be other than the
+ # supplied value.
# * <tt>:odd</tt> - Specifies the value must be an odd number.
# * <tt>:even</tt> - Specifies the value must be an even number.
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
- # the validation should occur (e.g. <tt>:if => :allow_validation</tt>, or
- # <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
- # or string should return or evaluate to a true or false value.
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
- # if the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
- # or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
- # method, proc or string should return or evaluate to a true or false value.
- # * <tt>:strict</tt> - Specifies whether validation should be strict.
- # See <tt>ActiveModel::Validation#validates!</tt> for more information.
+ #
+ # There is also a list of default options supported by every validator:
+ # +:if+, +:unless+, +:on+ and +:strict+ .
+ # See <tt>ActiveModel::Validation#validates</tt> for more information
#
# The following checks can also be supplied with a proc or a symbol which
# corresponds to a method:
@@ -134,8 +126,8 @@ module ActiveModel
# For example:
#
# class Person < ActiveRecord::Base
- # validates_numericality_of :width, :less_than => Proc.new { |person| person.height }
- # validates_numericality_of :width, :greater_than => :minimum_weight
+ # validates_numericality_of :width, less_than: Proc.new { |person| person.height }
+ # validates_numericality_of :width, greater_than: :minimum_weight
# end
def validates_numericality_of(*attr_names)
validates_with NumericalityValidator, _merge_attributes(attr_names)
diff --git a/activemodel/lib/active_model/validations/presence.rb b/activemodel/lib/active_model/validations/presence.rb
index a7dcdbba3d..4592a1deb0 100644
--- a/activemodel/lib/active_model/validations/presence.rb
+++ b/activemodel/lib/active_model/validations/presence.rb
@@ -20,28 +20,19 @@ module ActiveModel
#
# The first_name attribute must be in the object and it cannot be blank.
#
- # If you want to validate the presence of a boolean field (where the real values
- # are true and false), you will want to use
- # <tt>validates_inclusion_of :field_name, :in => [true, false]</tt>.
+ # If you want to validate the presence of a boolean field (where the real
+ # values are +true+ and +false+), you will want to use
+ # <tt>validates_inclusion_of :field_name, in: [true, false]</tt>.
#
# This is due to the way Object#blank? handles boolean values:
# <tt>false.blank? # => true</tt>.
#
# Configuration options:
# * <tt>:message</tt> - A custom error message (default is: "can't be blank").
- # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
- # validation contexts by default (+nil+), other options are <tt>:create</tt>
- # and <tt>:update</tt>.
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
- # the validation should occur (e.g. <tt>:if => :allow_validation</tt>, or
- # <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
- # or string should return or evaluate to a true or false value.
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
- # if the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
- # or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
- # proc or string should return or evaluate to a true or false value.
- # * <tt>:strict</tt> - Specifies whether validation should be strict.
- # See <tt>ActiveModel::Validation#validates!</tt> for more information.
+ #
+ # There is also a list of default options supported by every validator:
+ # +:if+, +:unless+, +:on+ and +:strict+.
+ # See <tt>ActiveModel::Validation#validates</tt> for more information
def validates_presence_of(*attr_names)
validates_with PresenceValidator, _merge_attributes(attr_names)
end
diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb
index 6c13d2b4a2..ecda9dffcf 100644
--- a/activemodel/lib/active_model/validations/validates.rb
+++ b/activemodel/lib/active_model/validations/validates.rb
@@ -69,14 +69,31 @@ module ActiveModel
# validator's initializer as +options[:in]+ while other types including
# regular expressions and strings are passed as +options[:with]+
#
+ # There is also a list of options that could be used along with validators:
+ # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
+ # validation contexts by default (+nil+), other options are <tt>:create</tt>
+ # and <tt>:update</tt>.
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
+ # if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
+ # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
+ # proc or string should return or evaluate to a +true+ or +false+ value.
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
+ # if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
+ # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
+ # method, proc or string should return or evaluate to a +true+ or
+ # +false+ value.
+ # * <tt>:strict</tt> - Specifies whether validation should be strict.
+ # See <tt>ActiveModel::Validation#validates!</tt> for more information.
+ #
+ # Example:
+ #
+ # validates :password, :presence => true, :confirmation => true, :if => :password_required?
+ #
# Finally, the options +:if+, +:unless+, +:on+, +:allow_blank+, +:allow_nil+ and +:strict+
# can be given to one specific validator, as a hash:
#
# validates :password, :presence => { :if => :password_required? }, :confirmation => true
#
- # Or to all at the same time:
- #
- # validates :password, :presence => true, :confirmation => true, :if => :password_required?
#
def validates(*attributes)
defaults = attributes.extract_options!.dup
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index 2953126c3c..d5d0798704 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -129,7 +129,7 @@ module ActiveModel #:nodoc:
# record, attribute and value.
#
# All Active Model validations are built on top of this validator.
- class EachValidator < Validator
+ class EachValidator < Validator #:nodoc:
attr_reader :attributes
# Returns a new validator instance. All options will be available via the
@@ -168,7 +168,7 @@ module ActiveModel #:nodoc:
# +BlockValidator+ is a special +EachValidator+ which receives a block on initialization
# and call this block for each attribute being validated. +validates_each+ uses this validator.
- class BlockValidator < EachValidator
+ class BlockValidator < EachValidator #:nodoc:
def initialize(options, &block)
@block = block
super
diff --git a/activemodel/test/cases/validations/callbacks_test.rb b/activemodel/test/cases/validations/callbacks_test.rb
index e4f602bd80..0015b3c196 100644
--- a/activemodel/test/cases/validations/callbacks_test.rb
+++ b/activemodel/test/cases/validations/callbacks_test.rb
@@ -20,7 +20,7 @@ class DogWithMethodCallbacks < Dog
def set_after_validation_marker; self.history << 'after_validation_marker' ; end
end
-class DogValidtorsAreProc < Dog
+class DogValidatorsAreProc < Dog
before_validation { self.history << 'before_validation_marker' }
after_validation { self.history << 'after_validation_marker' }
end
@@ -49,7 +49,7 @@ class CallbacksWithMethodNamesShouldBeCalled < ActiveModel::TestCase
end
def test_before_validation_and_after_validation_callbacks_should_be_called_with_proc
- d = DogValidtorsAreProc.new
+ d = DogValidatorsAreProc.new
d.valid?
assert_equal ['before_validation_marker', 'after_validation_marker'], d.history
end
diff --git a/activemodel/test/cases/validations/exclusion_validation_test.rb b/activemodel/test/cases/validations/exclusion_validation_test.rb
index adab8ccb2b..baccf72ecb 100644
--- a/activemodel/test/cases/validations/exclusion_validation_test.rb
+++ b/activemodel/test/cases/validations/exclusion_validation_test.rb
@@ -28,6 +28,16 @@ class ExclusionValidationTest < ActiveModel::TestCase
assert_equal ["option monkey is restricted"], t.errors[:title]
end
+ def test_validates_exclusion_of_with_within_option
+ Topic.validates_exclusion_of( :title, :within => %w( abe monkey ) )
+
+ assert Topic.new("title" => "something", "content" => "abc")
+
+ t = Topic.new("title" => "monkey")
+ assert t.invalid?
+ assert t.errors[:title].any?
+ end
+
def test_validates_exclusion_of_for_ruby_class
Person.validates_exclusion_of :karma, :in => %w( abe monkey )
diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb
index bb751cf9c5..4f8b7327c0 100644
--- a/activemodel/test/cases/validations/i18n_validation_test.rb
+++ b/activemodel/test/cases/validations/i18n_validation_test.rb
@@ -159,6 +159,17 @@ class I18nValidationTest < ActiveModel::TestCase
end
end
+ # validates_inclusion_of using :within w/ mocha
+
+ COMMON_CASES.each do |name, validation_options, generate_message_options|
+ test "validates_inclusion_of using :within on generated message #{name}" do
+ Person.validates_inclusion_of :title, validation_options.merge(:within => %w(a b c))
+ @person.title = 'z'
+ @person.errors.expects(:generate_message).with(:title, :inclusion, generate_message_options.merge(:value => 'z'))
+ @person.valid?
+ end
+ end
+
# validates_exclusion_of w/ mocha
COMMON_CASES.each do |name, validation_options, generate_message_options|
@@ -170,6 +181,17 @@ class I18nValidationTest < ActiveModel::TestCase
end
end
+ # validates_exclusion_of using :within w/ mocha
+
+ COMMON_CASES.each do |name, validation_options, generate_message_options|
+ test "validates_exclusion_of using :within generated message #{name}" do
+ Person.validates_exclusion_of :title, validation_options.merge(:within => %w(a b c))
+ @person.title = 'a'
+ @person.errors.expects(:generate_message).with(:title, :exclusion, generate_message_options.merge(:value => 'a'))
+ @person.valid?
+ end
+ end
+
# validates_numericality_of without :only_integer w/ mocha
COMMON_CASES.each do |name, validation_options, generate_message_options|
diff --git a/activemodel/test/cases/validations/inclusion_validation_test.rb b/activemodel/test/cases/validations/inclusion_validation_test.rb
index 851d345eab..c57fa75faf 100644
--- a/activemodel/test/cases/validations/inclusion_validation_test.rb
+++ b/activemodel/test/cases/validations/inclusion_validation_test.rb
@@ -60,6 +60,16 @@ class InclusionValidationTest < ActiveModel::TestCase
assert_equal ["option uhoh is not in the list"], t.errors[:title]
end
+ def test_validates_inclusion_of_with_within_option
+ Topic.validates_inclusion_of( :title, :within => %w( a b c d e f g ) )
+
+ assert Topic.new("title" => "a", "content" => "abc").valid?
+
+ t = Topic.new("title" => "uhoh", "content" => "abc")
+ assert t.invalid?
+ assert t.errors[:title].any?
+ end
+
def test_validates_inclusion_of_for_ruby_class
Person.validates_inclusion_of :karma, :in => %w( abe monkey )
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index f41ccfe56f..987d881821 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,66 @@
## Rails 4.0.0 (unreleased) ##
+* `Model.all` now returns an `ActiveRecord::Relation`, rather than an
+ array of records. Use `Model.to_a` or `Relation#to_a` if you really
+ want an array.
+
+ In some specific cases, this may cause breakage when upgrading.
+ However in most cases the `ActiveRecord::Relation` will just act as a
+ lazy-loaded array and there will be no problems.
+
+ Note that calling `Model.all` with options (e.g.
+ `Model.all(conditions: '...')` was already deprecated, but it will
+ still return an array in order to make the transition easier.
+
+ `Model.scoped` is deprecated in favour of `Model.all`.
+
+ `Relation#all` still returns an array, but is deprecated (since it
+ would serve no purpose if we made it return a `Relation`).
+
+ *Jon Leighton*
+
+* Deprecate `update_column` method in favor of `update_columns`.
+
+ *Rafael Mendonça França*
+
+* Added an `update_columns` method. This new method updates the given attributes on an object,
+ without calling save, hence skipping validations and callbacks.
+ Example:
+
+ User.first.update_columns({:name => "sebastian", :age => 25}) # => true
+
+ *Sebastian Martinez + Rafael Mendonça França*
+
+* Removed `:finder_sql` and `:counter_sql` collection association options. Please
+ use scopes instead.
+
+ *Jon Leighton*
+
+* Removed `:insert_sql` and `:delete_sql` `has_and_belongs_to_many`
+ association options. Please use `has_many :through` instead.
+
+ *Jon Leighton*
+
+* The migration generator now creates a join table with (commented) indexes every time
+ the migration name contains the word `join_table`:
+
+ rails g migration create_join_table_for_artists_and_musics artist_id:index music_id
+
+ *Aleksey Magusev*
+
+* Add `add_reference` and `remove_reference` schema statements. Aliases, `add_belongs_to`
+ and `remove_belongs_to` are acceptable. References are reversible.
+ Examples:
+
+ # Create a user_id column
+ add_reference(:products, :user)
+ # Create a supplier_id, supplier_type columns and appropriate index
+ add_reference(:products, :supplier, polymorphic: true, index: true)
+ # Remove polymorphic reference
+ remove_reference(:products, :supplier, polymorphic: true)
+
+ *Aleksey Magusev*
+
* Add `:default` and `:null` options to `column_exists?`.
column_exists?(:testings, :taggable_id, :integer, null: false)
@@ -7,27 +68,31 @@
*Aleksey Magusev*
-* `ActiveRelation#inspect` no longer calls `#to_a`. This means that in places
- where `#inspect` is implied (such as in the console), creating a relation
- will not execute it anymore, you'll have to call `#to_a` when necessary:
+* `ActiveRecord::Relation#inspect` now makes it clear that you are
+ dealing with a `Relation` object rather than an array:.
+
+ User.where(:age => 30).inspect
+ # => <ActiveRecord::Relation [#<User ...>, #<User ...>, ...]>
- User.where(:age => 30) # => returns the relation
- User.where(:age => 30).to_a # => executes the query and returns the loaded objects, as before
+ User.where(:age => 30).to_a.inspect
+ # => [#<User ...>, #<User ...>]
- *Brian Cardarella*
+ The number of records displayed will be limited to 10.
+
+ *Brian Cardarella, Jon Leighton & Damien Mathieu*
* Add `collation` and `ctype` support to PostgreSQL. These are available for PostgreSQL 8.4 or later.
Example:
- development:
- adapter: postgresql
- host: localhost
- database: rails_development
- username: foo
- password: bar
- encoding: UTF8
- collation: ja_JP.UTF8
- ctype: ja_JP.UTF8
+ development:
+ adapter: postgresql
+ host: localhost
+ database: rails_development
+ username: foo
+ password: bar
+ encoding: UTF8
+ collation: ja_JP.UTF8
+ ctype: ja_JP.UTF8
*kennyj*
@@ -145,7 +210,7 @@
* Add uuid datatype support to PostgreSQL adapter. *Konstantin Shabanov*
-* `update_attribute` has been removed. Use `update_column` if
+* `update_attribute` has been removed. Use `update_columns` if
you want to bypass mass-assignment protection, validations, callbacks,
and touching of updated_at. Otherwise please use `update_attributes`.
@@ -210,13 +275,12 @@
Note that as an interim step, it is possible to rewrite the above as:
- Post.scoped(:where => { :comments_count => 10 }, :limit => 5)
+ Post.all.merge(:where => { :comments_count => 10 }, :limit => 5)
This could save you a lot of work if there is a lot of old-style
finder usage in your application.
- Calling `Post.scoped(options)` is a shortcut for
- `Post.scoped.merge(options)`. `Relation#merge` now accepts a hash of
+ `Relation#merge` now accepts a hash of
options, but they must be identical to the names of the equivalent
finder method. These are mostly identical to the old-style finder
option names, except in the following cases:
@@ -859,7 +923,7 @@
* LRU cache in mysql and sqlite are now per-process caches.
- * lib/active_record/connection_adapters/mysql_adapter.rb: LRU cache keys are per process id.
+ * lib/active_record/connection_adapters/mysql_adapter.rb: LRU cache keys are per process id.
* lib/active_record/connection_adapters/sqlite_adapter.rb: ditto
*Aaron Patterson*
diff --git a/activerecord/RUNNING_UNIT_TESTS b/activerecord/RUNNING_UNIT_TESTS
index 2c310e7ac3..bdd8834dcb 100644
--- a/activerecord/RUNNING_UNIT_TESTS
+++ b/activerecord/RUNNING_UNIT_TESTS
@@ -1,11 +1,10 @@
-== Configure databases
+== Setup
-Copy test/config.example.yml to test/config.yml and edit as needed. Or just run the tests for
-the first time, which will do the copy automatically and use the default (sqlite3).
+If you don't have the environment set make sure to read
-You can build postgres and mysql databases using the postgresql:build_databases and mysql:build_databases rake tasks.
+ http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#testing-active-record.
-== Running the tests
+== Running the Tests
You can run a particular test file from the command line, e.g.
@@ -26,7 +25,7 @@ You can run all the tests for a given database via rake:
The 'rake test' task will run all the tests for mysql, mysql2, sqlite3 and postgresql.
-== Config file
+== Custom Config file
By default, the config file is expected to be at the path test/config.yml. You can specify a
custom location with the ARCONFIG environment variable.
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index a62fce4756..05715feb97 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -104,6 +104,7 @@ module ActiveRecord
# See ActiveRecord::Associations::ClassMethods for documentation.
module Associations # :nodoc:
+ extend ActiveSupport::Autoload
extend ActiveSupport::Concern
# These classes will be loaded when associations are created.
@@ -133,11 +134,13 @@ module ActiveRecord
autoload :HasAndBelongsToMany, 'active_record/associations/builder/has_and_belongs_to_many'
end
- autoload :Preloader, 'active_record/associations/preloader'
- autoload :JoinDependency, 'active_record/associations/join_dependency'
- autoload :AssociationScope, 'active_record/associations/association_scope'
- autoload :AliasTracker, 'active_record/associations/alias_tracker'
- autoload :JoinHelper, 'active_record/associations/join_helper'
+ eager_autoload do
+ autoload :Preloader, 'active_record/associations/preloader'
+ autoload :JoinDependency, 'active_record/associations/join_dependency'
+ autoload :AssociationScope, 'active_record/associations/association_scope'
+ autoload :AliasTracker, 'active_record/associations/alias_tracker'
+ autoload :JoinHelper, 'active_record/associations/join_helper'
+ end
# Clears out the association cache.
def clear_association_cache #:nodoc:
@@ -1104,15 +1107,6 @@ module ActiveRecord
# a +belongs_to+, and the records which get deleted are the join records, rather than
# the associated records.
#
- # [:finder_sql]
- # Specify a complete SQL statement to fetch the association. This is a good way to go for complex
- # associations that depend on multiple tables. May be supplied as a string or a proc where interpolation is
- # required. Note: When this option is used, +find_in_collection+
- # is _not_ added.
- # [:counter_sql]
- # Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
- # specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by
- # replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
# [:extend]
# Specify a named module for extending the proxy. See "Association extensions".
# [:include]
@@ -1184,16 +1178,8 @@ module ActiveRecord
# has_many :tags, :as => :taggable
# has_many :reports, :readonly => true
# has_many :subscribers, :through => :subscriptions, :source => :user
- # has_many :subscribers, :class_name => "Person", :finder_sql => Proc.new {
- # %Q{
- # SELECT DISTINCT *
- # FROM people p, post_subscriptions ps
- # WHERE ps.post_id = #{id} AND ps.person_id = p.id
- # ORDER BY p.first_name
- # }
- # }
- def has_many(name, options = {}, &extension)
- Builder::HasMany.build(self, name, options, &extension)
+ def has_many(name, scope = nil, options = {}, &extension)
+ Builder::HasMany.build(self, name, scope, options, &extension)
end
# Specifies a one-to-one association with another class. This method should only be used
@@ -1308,8 +1294,8 @@ module ActiveRecord
# has_one :boss, :readonly => :true
# has_one :club, :through => :membership
# has_one :primary_address, :through => :addressables, :conditions => ["addressable.primary = ?", true], :source => :addressable
- def has_one(name, options = {})
- Builder::HasOne.build(self, name, options)
+ def has_one(name, scope = nil, options = {})
+ Builder::HasOne.build(self, name, scope, options)
end
# Specifies a one-to-one association with another class. This method should only be used
@@ -1431,8 +1417,8 @@ module ActiveRecord
# belongs_to :post, :counter_cache => true
# belongs_to :company, :touch => true
# belongs_to :company, :touch => :employees_last_updated_at
- def belongs_to(name, options = {})
- Builder::BelongsTo.build(self, name, options)
+ def belongs_to(name, scope = nil, options = {})
+ Builder::BelongsTo.build(self, name, scope, options)
end
# Specifies a many-to-many relationship with another class. This associates two classes via an
@@ -1555,18 +1541,6 @@ module ActiveRecord
# such as <tt>last_name, first_name DESC</tt>
# [:uniq]
# If true, duplicate associated objects will be ignored by accessors and query methods.
- # [:finder_sql]
- # Overwrite the default generated SQL statement used to fetch the association with a manual statement
- # [:counter_sql]
- # Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
- # specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by
- # replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
- # [:delete_sql]
- # Overwrite the default generated SQL statement used to remove links between the associated
- # classes with a manual statement.
- # [:insert_sql]
- # Overwrite the default generated SQL statement used to add links between the associated classes
- # with a manual statement.
# [:extend]
# Anonymous module for extending the proxy, see "Association extensions".
# [:include]
@@ -1603,10 +1577,8 @@ module ActiveRecord
# has_and_belongs_to_many :nations, :class_name => "Country"
# has_and_belongs_to_many :categories, :join_table => "prods_cats"
# has_and_belongs_to_many :categories, :readonly => true
- # has_and_belongs_to_many :active_projects, :join_table => 'developers_projects', :delete_sql =>
- # proc { |record| "DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}" }
- def has_and_belongs_to_many(name, options = {}, &extension)
- Builder::HasAndBelongsToMany.build(self, name, options, &extension)
+ def has_and_belongs_to_many(name, scope = nil, options = {}, &extension)
+ Builder::HasAndBelongsToMany.build(self, name, scope, options, &extension)
end
end
end
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index e75003f261..9e464ff681 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -118,7 +118,7 @@ module ActiveRecord
# Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
# through association's scope)
def target_scope
- klass.scoped
+ klass.all
end
# Loads the \target if needed and returns it.
@@ -140,12 +140,19 @@ module ActiveRecord
reset
end
- def interpolate(sql, record = nil)
- if sql.respond_to?(:to_proc)
- owner.send(:instance_exec, record, &sql)
- else
- sql
- end
+ # We can't dump @reflection since it contains the scope proc
+ def marshal_dump
+ reflection = @reflection
+ @reflection = nil
+
+ ivars = instance_variables.map { |name| [name, instance_variable_get(name)] }
+ [reflection.name, ivars]
+ end
+
+ def marshal_load(data)
+ reflection_name, ivars = data
+ ivars.each { |name, val| instance_variable_set(name, val) }
+ @reflection = @owner.class.reflect_on_association(reflection_name)
end
private
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb
index 89a626693d..cb97490ef1 100644
--- a/activerecord/lib/active_record/associations/association_scope.rb
+++ b/activerecord/lib/active_record/associations/association_scope.rb
@@ -5,8 +5,8 @@ module ActiveRecord
attr_reader :association, :alias_tracker
- delegate :klass, :owner, :reflection, :interpolate, :to => :association
- delegate :chain, :conditions, :options, :source_options, :active_record, :to => :reflection
+ delegate :klass, :owner, :reflection, :to => :association
+ delegate :chain, :scope_chain, :options, :source_options, :active_record, :to => :reflection
def initialize(association)
@association = association
@@ -15,20 +15,7 @@ module ActiveRecord
def scope
scope = klass.unscoped
-
- scope.extending!(*Array(options[:extend]))
-
- # It's okay to just apply all these like this. The options will only be present if the
- # association supports that option; this is enforced by the association builder.
- scope.merge!(options.slice(
- :readonly, :references, :order, :limit, :joins, :group, :having, :offset, :select, :uniq))
-
- if options[:include]
- scope.includes! options[:include]
- elsif options[:through]
- scope.includes! source_options[:include]
- end
-
+ scope.merge! eval_scope(klass, reflection.scope) if reflection.scope
add_constraints(scope)
end
@@ -82,8 +69,6 @@ module ActiveRecord
foreign_key = reflection.active_record_primary_key
end
- conditions = self.conditions[i]
-
if reflection == chain.last
bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key]
scope = scope.where(table[key].eq(bind_val))
@@ -93,14 +78,6 @@ module ActiveRecord
bind_val = bind scope, table.table_name, reflection.type.to_s, value
scope = scope.where(table[reflection.type].eq(bind_val))
end
-
- conditions.each do |condition|
- if options[:through] && condition.is_a?(Hash)
- condition = disambiguate_condition(table, condition)
- end
-
- scope = scope.where(interpolate(condition))
- end
else
constraint = table[key].eq(foreign_table[foreign_key])
@@ -110,13 +87,15 @@ module ActiveRecord
end
scope = scope.joins(join(foreign_table, constraint))
+ end
- conditions.each do |condition|
- condition = interpolate(condition)
- condition = disambiguate_condition(table, condition) unless i == 0
+ # Exclude the scope of the association itself, because that
+ # was already merged in the #scope method.
+ (scope_chain[i] - [self.reflection.scope]).each do |scope_chain_item|
+ item = eval_scope(reflection.klass, scope_chain_item)
- scope = scope.where(condition)
- end
+ scope.includes! item.includes_values
+ scope.where_values += item.where_values
end
end
@@ -138,19 +117,11 @@ module ActiveRecord
end
end
- def disambiguate_condition(table, condition)
- if condition.is_a?(Hash)
- Hash[
- condition.map do |k, v|
- if v.is_a?(Hash)
- [k, v]
- else
- [table.table_alias || table.name, { k => v }]
- end
- end
- ]
+ def eval_scope(klass, scope)
+ if scope.is_a?(Relation)
+ scope
else
- condition
+ klass.unscoped.instance_exec(owner, &scope)
end
end
end
diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb
index 9a6896dd55..f45ab1aff4 100644
--- a/activerecord/lib/active_record/associations/builder/association.rb
+++ b/activerecord/lib/active_record/associations/builder/association.rb
@@ -1,36 +1,61 @@
module ActiveRecord::Associations::Builder
class Association #:nodoc:
- class_attribute :valid_options
- self.valid_options = [:class_name, :foreign_key, :select, :conditions, :include, :extend, :readonly, :validate, :references]
+ class << self
+ attr_accessor :valid_options
+ end
- # Set by subclasses
- class_attribute :macro
+ self.valid_options = [:class_name, :foreign_key, :validate]
- attr_reader :model, :name, :options, :reflection
+ attr_reader :model, :name, :scope, :options, :reflection
- def self.build(model, name, options)
- new(model, name, options).build
+ def self.build(*args, &block)
+ new(*args, &block).build
end
- def initialize(model, name, options)
- @model, @name, @options = model, name, options
+ def initialize(model, name, scope, options)
+ @model = model
+ @name = name
+
+ if scope.is_a?(Hash)
+ @scope = nil
+ @options = scope
+ else
+ @scope = scope
+ @options = options
+ end
+
+ if @scope && @scope.arity == 0
+ prev_scope = @scope
+ @scope = proc { instance_exec(&prev_scope) }
+ end
end
def mixin
@model.generated_feature_methods
end
+ include Module.new { def build; end }
+
def build
validate_options
- reflection = model.create_reflection(self.class.macro, name, options, model)
define_accessors
- reflection
+ @reflection = model.create_reflection(macro, name, scope, options, model)
+ super # provides an extension point
+ @reflection
+ end
+
+ def macro
+ raise NotImplementedError
+ end
+
+ def valid_options
+ Association.valid_options
end
private
def validate_options
- options.assert_valid_keys(self.class.valid_options)
+ options.assert_valid_keys(valid_options)
end
def define_accessors
diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb
index 4183c222de..4bef996297 100644
--- a/activerecord/lib/active_record/associations/builder/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb
@@ -2,9 +2,13 @@ require 'active_support/core_ext/object/inclusion'
module ActiveRecord::Associations::Builder
class BelongsTo < SingularAssociation #:nodoc:
- self.macro = :belongs_to
+ def macro
+ :belongs_to
+ end
- self.valid_options += [:foreign_type, :polymorphic, :touch]
+ def valid_options
+ super + [:foreign_type, :polymorphic, :touch]
+ end
def constructable?
!options[:polymorphic]
diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb
index 768f70b6c9..b28d6a746c 100644
--- a/activerecord/lib/active_record/associations/builder/collection_association.rb
+++ b/activerecord/lib/active_record/associations/builder/collection_association.rb
@@ -2,19 +2,14 @@ module ActiveRecord::Associations::Builder
class CollectionAssociation < Association #:nodoc:
CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
- self.valid_options += [
- :table_name, :order, :group, :having, :limit, :offset, :uniq, :finder_sql,
- :counter_sql, :before_add, :after_add, :before_remove, :after_remove
- ]
-
- attr_reader :block_extension
-
- def self.build(model, name, options, &extension)
- new(model, name, options, &extension).build
+ def valid_options
+ super + [:table_name, :before_add, :after_add, :before_remove, :after_remove]
end
- def initialize(model, name, options, &extension)
- super(model, name, options)
+ attr_reader :block_extension, :extension_module
+
+ def initialize(*args, &extension)
+ super(*args)
@block_extension = extension
end
@@ -32,18 +27,24 @@ module ActiveRecord::Associations::Builder
private
def wrap_block_extension
- options[:extend] = Array(options[:extend])
-
if block_extension
+ @extension_module = mod = Module.new(&block_extension)
silence_warnings do
- model.parent.const_set(extension_module_name, Module.new(&block_extension))
+ model.parent.const_set(extension_module_name, mod)
+ end
+
+ prev_scope = @scope
+
+ if prev_scope
+ @scope = proc { |owner| instance_exec(owner, &prev_scope).extending(mod) }
+ else
+ @scope = proc { extending(mod) }
end
- options[:extend].push("#{model.parent}::#{extension_module_name}".constantize)
end
end
def extension_module_name
- @extension_module_name ||= "#{model.to_s.demodulize}#{name.to_s.camelize}AssociationExtension"
+ @extension_module_name ||= "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
end
def define_callback(callback_name)
diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
index f7656ecd47..a30e2dab26 100644
--- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
@@ -1,8 +1,12 @@
module ActiveRecord::Associations::Builder
class HasAndBelongsToMany < CollectionAssociation #:nodoc:
- self.macro = :has_and_belongs_to_many
+ def macro
+ :has_and_belongs_to_many
+ end
- self.valid_options += [:join_table, :association_foreign_key, :delete_sql, :insert_sql]
+ def valid_options
+ super + [:join_table, :association_foreign_key]
+ end
def build
reflection = super
diff --git a/activerecord/lib/active_record/associations/builder/has_many.rb b/activerecord/lib/active_record/associations/builder/has_many.rb
index d37d4e9d33..81df1fb135 100644
--- a/activerecord/lib/active_record/associations/builder/has_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_many.rb
@@ -2,9 +2,13 @@ require 'active_support/core_ext/object/inclusion'
module ActiveRecord::Associations::Builder
class HasMany < CollectionAssociation #:nodoc:
- self.macro = :has_many
+ def macro
+ :has_many
+ end
- self.valid_options += [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of]
+ def valid_options
+ super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of]
+ end
def build
reflection = super
diff --git a/activerecord/lib/active_record/associations/builder/has_one.rb b/activerecord/lib/active_record/associations/builder/has_one.rb
index bc8a212bee..cdb45e8e58 100644
--- a/activerecord/lib/active_record/associations/builder/has_one.rb
+++ b/activerecord/lib/active_record/associations/builder/has_one.rb
@@ -2,12 +2,15 @@ require 'active_support/core_ext/object/inclusion'
module ActiveRecord::Associations::Builder
class HasOne < SingularAssociation #:nodoc:
- self.macro = :has_one
-
- self.valid_options += [:order, :as]
+ def macro
+ :has_one
+ end
- class_attribute :through_options
- self.through_options = [:through, :source, :source_type]
+ def valid_options
+ valid = super + [:order, :as]
+ valid += [:through, :source, :source_type] if options[:through]
+ valid
+ end
def constructable?
!options[:through]
@@ -21,12 +24,6 @@ module ActiveRecord::Associations::Builder
private
- def validate_options
- valid_options = self.class.valid_options
- valid_options += self.class.through_options if options[:through]
- options.assert_valid_keys(valid_options)
- end
-
def configure_dependency
if options[:dependent]
unless options[:dependent].in?([:destroy, :delete, :nullify, :restrict])
diff --git a/activerecord/lib/active_record/associations/builder/singular_association.rb b/activerecord/lib/active_record/associations/builder/singular_association.rb
index 436b6c1524..90a4b7c2ef 100644
--- a/activerecord/lib/active_record/associations/builder/singular_association.rb
+++ b/activerecord/lib/active_record/associations/builder/singular_association.rb
@@ -1,6 +1,8 @@
module ActiveRecord::Associations::Builder
class SingularAssociation < Association #:nodoc:
- self.valid_options += [:remote, :dependent, :counter_cache, :primary_key, :inverse_of]
+ def valid_options
+ super + [:remote, :dependent, :counter_cache, :primary_key, :inverse_of]
+ end
def constructable?
true
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 2f6ddfeeb3..30522e3a5d 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -44,7 +44,7 @@ module ActiveRecord
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
def ids_reader
- if loaded? || options[:finder_sql]
+ if loaded?
load_target.map do |record|
record.send(reflection.association_primary_key)
end
@@ -79,11 +79,7 @@ module ActiveRecord
if block_given?
load_target.find(*args) { |*block_args| yield(*block_args) }
else
- if options[:finder_sql]
- find_by_scan(*args)
- else
- scoped.find(*args)
- end
+ scoped.find(*args)
end
end
@@ -170,35 +166,26 @@ module ActiveRecord
end
end
- # Count all records using SQL. If the +:counter_sql+ or +:finder_sql+ option is set for the
- # association, it will be used for the query. Otherwise, construct options and pass them with
+ # Count all records using SQL. Construct options and pass them with
# scope to the target class's +count+.
def count(column_name = nil, count_options = {})
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
- if options[:counter_sql] || options[:finder_sql]
- unless count_options.blank?
- raise ArgumentError, "If finder_sql/counter_sql is used then options cannot be passed"
- end
-
- reflection.klass.count_by_sql(custom_counter_sql)
- else
- if options[:uniq]
- # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
- column_name ||= reflection.klass.primary_key
- count_options[:distinct] = true
- end
+ if association_scope.uniq_value
+ # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
+ column_name ||= reflection.klass.primary_key
+ count_options[:distinct] = true
+ end
- value = scoped.count(column_name, count_options)
+ value = scoped.count(column_name, count_options)
- limit = options[:limit]
- offset = options[:offset]
+ limit = options[:limit]
+ offset = options[:offset]
- if limit || offset
- [ [value - offset.to_i, 0].max, limit.to_i ].min
- else
- value
- end
+ if limit || offset
+ [ [value - offset.to_i, 0].max, limit.to_i ].min
+ else
+ value
end
end
@@ -246,14 +233,14 @@ module ActiveRecord
# +count_records+, which is a method descendants have to provide.
def size
if !find_target? || loaded?
- if options[:uniq]
+ if association_scope.uniq_value
target.uniq.size
else
target.size
end
- elsif !loaded? && options[:group]
+ elsif !loaded? && !association_scope.group_values.empty?
load_target.size
- elsif !loaded? && !options[:uniq] && target.is_a?(Array)
+ elsif !loaded? && !association_scope.uniq_value && target.is_a?(Array)
unsaved_records = target.select { |r| r.new_record? }
unsaved_records.size + count_records
else
@@ -298,9 +285,9 @@ module ActiveRecord
end
end
- def uniq(collection = load_target)
+ def uniq
seen = {}
- collection.find_all do |record|
+ load_target.find_all do |record|
seen[record.id] = true unless seen.key?(record.id)
end
end
@@ -323,7 +310,6 @@ module ActiveRecord
if record.new_record?
include_in_memory?(record)
else
- load_target if options[:finder_sql]
loaded? ? target.include?(record) : scoped.exists?(record)
end
else
@@ -344,7 +330,7 @@ module ActiveRecord
callback(:before_add, record)
yield(record) if block_given?
- if options[:uniq] && index = @target.index(record)
+ if association_scope.uniq_value && index = @target.index(record)
@target[index] = record
else
@target << record
@@ -358,32 +344,8 @@ module ActiveRecord
private
- def custom_counter_sql
- if options[:counter_sql]
- interpolate(options[:counter_sql])
- else
- # replace the SELECT clause with COUNT(SELECTS), preserving any hints within /* ... */
- interpolate(options[:finder_sql]).sub(/SELECT\b(\/\*.*?\*\/ )?(.*)\bFROM\b/im) do
- count_with = $2.to_s
- count_with = '*' if count_with.blank? || count_with =~ /,/
- "SELECT #{$1}COUNT(#{count_with}) FROM"
- end
- end
- end
-
- def custom_finder_sql
- interpolate(options[:finder_sql])
- end
-
def find_target
- records =
- if options[:finder_sql]
- reflection.klass.find_by_sql(custom_finder_sql)
- else
- scoped.all
- end
-
- records = options[:uniq] ? uniq(records) : records
+ records = scoped.to_a
records.each { |record| set_inverse_instance(record) }
records
end
@@ -522,7 +484,6 @@ module ActiveRecord
# Otherwise, go to the database only if none of the following are true:
# * target already loaded
# * owner is new record
- # * custom :finder_sql exists
# * target contains new or changed record(s)
# * the first arg is an integer (which indicates the number of records to be returned)
def fetch_first_or_last_using_find?(args)
@@ -531,7 +492,6 @@ module ActiveRecord
else
!(loaded? ||
owner.new_record? ||
- options[:finder_sql] ||
target.any? { |record| record.new_record? || record.changed? } ||
args.first.kind_of?(Integer))
end
@@ -548,20 +508,6 @@ module ActiveRecord
end
end
- # If using a custom finder_sql, #find scans the entire collection.
- def find_by_scan(*args)
- expects_array = args.first.kind_of?(Array)
- ids = args.flatten.compact.map{ |arg| arg.to_i }.uniq
-
- if ids.size == 1
- id = ids.first
- record = load_target.detect { |r| id == r.id }
- expects_array ? [ record ] : record
- else
- load_target.select { |r| ids.include?(r.id) }
- end
- end
-
# Fetches the first/last using SQL if possible, otherwise from the target array.
def first_or_last(type, *args)
args.shift if args.first.is_a?(Hash) && args.first.empty?
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 2fb80fdc4c..49891f7675 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -896,13 +896,9 @@ module ActiveRecord
end
def spawn
- scoped
- end
-
- def scoped(options = nil)
association = @association
- super.extending! do
+ @association.scoped.extending! do
define_method(:proxy_association) { association }
end
end
diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
index 93618721bb..e5b40f3911 100644
--- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
@@ -18,16 +18,12 @@ module ActiveRecord
end
end
- if options[:insert_sql]
- owner.connection.insert(interpolate(options[:insert_sql], record))
- else
- stmt = join_table.compile_insert(
- join_table[reflection.foreign_key] => owner.id,
- join_table[reflection.association_foreign_key] => record.id
- )
+ stmt = join_table.compile_insert(
+ join_table[reflection.foreign_key] => owner.id,
+ join_table[reflection.association_foreign_key] => record.id
+ )
- owner.connection.insert stmt
- end
+ owner.connection.insert stmt
record
end
@@ -39,22 +35,17 @@ module ActiveRecord
end
def delete_records(records, method)
- if sql = options[:delete_sql]
- records = load_target if records == :all
- records.each { |record| owner.connection.delete(interpolate(sql, record)) }
- else
- relation = join_table
- condition = relation[reflection.foreign_key].eq(owner.id)
-
- unless records == :all
- condition = condition.and(
- relation[reflection.association_foreign_key]
- .in(records.map { |x| x.id }.compact)
- )
- end
-
- owner.connection.delete(relation.where(condition).compile_delete)
+ relation = join_table
+ condition = relation[reflection.foreign_key].eq(owner.id)
+
+ unless records == :all
+ condition = condition.and(
+ relation[reflection.association_foreign_key]
+ .in(records.map { |x| x.id }.compact)
+ )
end
+
+ owner.connection.delete(relation.where(condition).compile_delete)
end
def invertible_for?(record)
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index e631579087..41c6ca92cc 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -33,20 +33,14 @@ module ActiveRecord
# If the collection is empty the target is set to an empty array and
# the loaded flag is set to true as well.
def count_records
- count = if has_cached_counter?
- owner.send(:read_attribute, cached_counter_attribute_name)
- elsif options[:counter_sql] || options[:finder_sql]
- reflection.klass.count_by_sql(custom_counter_sql)
- else
- scoped.count
- end
+ count = has_cached_counter? ? owner[cached_counter_attribute_name] : scoped.count
# If there's nothing in the database and @target has no new records
# we are certain the current target is an empty array. This is a
# documented side-effect of the method that may avoid an extra SELECT.
@target ||= [] and loaded! if count == 0
- [options[:limit], count].compact.min
+ [association_scope.limit_value, count].compact.min
end
def has_cached_counter?(reflection = reflection)
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index 2683aaf5da..6c5a9d73a9 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -171,7 +171,7 @@ module ActiveRecord
def find_target
return [] unless target_reflection_has_associated_record?
- scoped.all
+ scoped.to_a
end
# NOTE - not sure that we can actually cope with inverses here
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb
index f0d1120c68..7086dfa34c 100644
--- a/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_association.rb
@@ -36,7 +36,7 @@ module ActiveRecord
when :destroy
target.destroy
when :nullify
- target.update_column(reflection.foreign_key, nil)
+ target.update_columns(reflection.foreign_key => nil)
end
end
end
diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
index 0d7d28e458..0d3b4dbab1 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
@@ -92,14 +92,21 @@ module ActiveRecord
constraint = build_constraint(reflection, table, key, foreign_table, foreign_key)
- conditions = self.conditions[i].dup
- conditions << { reflection.type => foreign_klass.base_class.name } if reflection.type
+ scope_chain_items = scope_chain[i]
- conditions.each do |condition|
- condition = active_record.send(:sanitize_sql, interpolate(condition), table.table_alias || table.name)
- condition = Arel.sql(condition) unless condition.is_a?(Arel::Node)
+ if reflection.type
+ scope_chain_items += [
+ ActiveRecord::Relation.new(reflection.klass, table)
+ .where(reflection.type => foreign_klass.base_class.name)
+ ]
+ end
+
+ scope_chain_items.each do |item|
+ unless item.is_a?(Relation)
+ item = ActiveRecord::Relation.new(reflection.klass, table).instance_exec(self, &item)
+ end
- constraint = constraint.and(condition)
+ constraint = constraint.and(item.arel.constraints) unless item.arel.constraints.empty?
end
relation.from(join(table, constraint))
@@ -137,18 +144,8 @@ module ActiveRecord
table.table_alias || table.name
end
- def conditions
- @conditions ||= reflection.conditions.reverse
- end
-
- private
-
- def interpolate(conditions)
- if conditions.respond_to?(:to_proc)
- instance_eval(&conditions)
- else
- conditions
- end
+ def scope_chain
+ @scope_chain ||= reflection.scope_chain.reverse
end
end
diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb
index 54705e4950..ce5bf15f10 100644
--- a/activerecord/lib/active_record/associations/preloader.rb
+++ b/activerecord/lib/active_record/associations/preloader.rb
@@ -42,7 +42,7 @@ module ActiveRecord
autoload :HasAndBelongsToMany, 'active_record/associations/preloader/has_and_belongs_to_many'
autoload :BelongsTo, 'active_record/associations/preloader/belongs_to'
- attr_reader :records, :associations, :options, :model
+ attr_reader :records, :associations, :preload_scope, :model
# Eager loads the named associations for the given Active Record record(s).
#
@@ -78,15 +78,10 @@ module ActiveRecord
# [ :books, :author ]
# { :author => :avatar }
# [ :books, { :author => :avatar } ]
- #
- # +options+ contains options that will be passed to ActiveRecord::Base#find
- # (which is called under the hood for preloading records). But it is passed
- # only one level deep in the +associations+ argument, i.e. it's not passed
- # to the child associations when +associations+ is a Hash.
- def initialize(records, associations, options = {})
- @records = Array.wrap(records).compact.uniq
- @associations = Array.wrap(associations)
- @options = options
+ def initialize(records, associations, preload_scope = nil)
+ @records = Array.wrap(records).compact.uniq
+ @associations = Array.wrap(associations)
+ @preload_scope = preload_scope || Relation.new(nil, nil)
end
def run
@@ -110,7 +105,7 @@ module ActiveRecord
def preload_hash(association)
association.each do |parent, child|
- Preloader.new(records, parent, options).run
+ Preloader.new(records, parent, preload_scope).run
Preloader.new(records.map { |record| record.send(parent) }.flatten, child).run
end
end
@@ -125,7 +120,7 @@ module ActiveRecord
def preload_one(association)
grouped_records(association).each do |reflection, klasses|
klasses.each do |klass, records|
- preloader_for(reflection).new(klass, records, reflection, options).run
+ preloader_for(reflection).new(klass, records, reflection, preload_scope).run
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index b4c3908b10..fa77a8733b 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -2,16 +2,16 @@ module ActiveRecord
module Associations
class Preloader
class Association #:nodoc:
- attr_reader :owners, :reflection, :preload_options, :model, :klass
-
- def initialize(klass, owners, reflection, preload_options)
- @klass = klass
- @owners = owners
- @reflection = reflection
- @preload_options = preload_options || {}
- @model = owners.first && owners.first.class
- @scoped = nil
- @owners_by_key = nil
+ attr_reader :owners, :reflection, :preload_scope, :model, :klass
+
+ def initialize(klass, owners, reflection, preload_scope)
+ @klass = klass
+ @owners = owners
+ @reflection = reflection
+ @preload_scope = preload_scope
+ @model = owners.first && owners.first.class
+ @scoped = nil
+ @owners_by_key = nil
end
def run
@@ -92,34 +92,29 @@ module ActiveRecord
records_by_owner
end
+ def reflection_scope
+ @reflection_scope ||= reflection.scope ? klass.unscoped.instance_exec(nil, &reflection.scope) : klass.unscoped
+ end
+
def build_scope
scope = klass.unscoped
scope.default_scoped = true
- scope = scope.where(interpolate(options[:conditions]))
- scope = scope.where(interpolate(preload_options[:conditions]))
+ values = reflection_scope.values
+ preload_values = preload_scope.values
- scope = scope.select(preload_options[:select] || options[:select] || table[Arel.star])
- scope = scope.includes(preload_options[:include] || options[:include])
+ scope.where_values = Array(values[:where]) + Array(preload_values[:where])
+ scope.references_values = Array(values[:references]) + Array(preload_values[:references])
+
+ scope.select! preload_values[:select] || values[:select] || table[Arel.star]
+ scope.includes! preload_values[:includes] || values[:includes]
if options[:as]
- scope = scope.where(
- klass.table_name => {
- reflection.type => model.base_class.sti_name
- }
- )
+ scope.where!(klass.table_name => { reflection.type => model.base_class.sti_name })
end
scope
end
-
- def interpolate(conditions)
- if conditions.respond_to?(:to_proc)
- klass.send(:instance_eval, &conditions)
- else
- conditions
- end
- end
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/collection_association.rb b/activerecord/lib/active_record/associations/preloader/collection_association.rb
index c248aeaaf6..e6cd35e7a1 100644
--- a/activerecord/lib/active_record/associations/preloader/collection_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/collection_association.rb
@@ -6,7 +6,7 @@ module ActiveRecord
private
def build_scope
- super.order(preload_options[:order] || options[:order])
+ super.order(preload_scope.values[:order] || reflection_scope.values[:order])
end
def preload
diff --git a/activerecord/lib/active_record/associations/preloader/has_many_through.rb b/activerecord/lib/active_record/associations/preloader/has_many_through.rb
index c6e9ede356..9a662d3f53 100644
--- a/activerecord/lib/active_record/associations/preloader/has_many_through.rb
+++ b/activerecord/lib/active_record/associations/preloader/has_many_through.rb
@@ -6,7 +6,7 @@ module ActiveRecord
def associated_records_by_owner
super.each do |owner, records|
- records.uniq! if options[:uniq]
+ records.uniq! if reflection_scope.uniq_value
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/has_one.rb b/activerecord/lib/active_record/associations/preloader/has_one.rb
index 848448bb48..24728e9f01 100644
--- a/activerecord/lib/active_record/associations/preloader/has_one.rb
+++ b/activerecord/lib/active_record/associations/preloader/has_one.rb
@@ -14,7 +14,7 @@ module ActiveRecord
private
def build_scope
- super.order(preload_options[:order] || options[:order])
+ super.order(preload_scope.values[:order] || reflection_scope.values[:order])
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb
index ad6374d09a..1c1ba11c44 100644
--- a/activerecord/lib/active_record/associations/preloader/through_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/through_association.rb
@@ -14,10 +14,7 @@ module ActiveRecord
def associated_records_by_owner
through_records = through_records_by_owner
- ActiveRecord::Associations::Preloader.new(
- through_records.values.flatten,
- source_reflection.name, options
- ).run
+ Preloader.new(through_records.values.flatten, source_reflection.name, reflection_scope).run
through_records.each do |owner, records|
records.map! { |r| r.send(source_reflection.name) }.flatten!
@@ -28,10 +25,7 @@ module ActiveRecord
private
def through_records_by_owner
- ActiveRecord::Associations::Preloader.new(
- owners, through_reflection.name,
- through_options
- ).run
+ Preloader.new(owners, through_reflection.name, through_scope).run
Hash[owners.map do |owner|
through_records = Array.wrap(owner.send(through_reflection.name))
@@ -45,21 +39,22 @@ module ActiveRecord
end]
end
- def through_options
- through_options = {}
+ def through_scope
+ through_scope = through_reflection.klass.unscoped
if options[:source_type]
- through_options[:conditions] = { reflection.foreign_type => options[:source_type] }
+ through_scope.where! reflection.foreign_type => options[:source_type]
else
- if options[:conditions]
- through_options[:include] = options[:include] || options[:source]
- through_options[:conditions] = options[:conditions]
+ unless reflection_scope.where_values.empty?
+ through_scope.includes_values = reflection_scope.values[:includes] || options[:source]
+ through_scope.where_values = reflection_scope.values[:where]
end
- through_options[:order] = options[:order]
+ through_scope.order! reflection_scope.values[:order]
+ through_scope.references! reflection_scope.values[:references]
end
- through_options
+ through_scope
end
end
end
diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb
index be890e5767..b9e014735b 100644
--- a/activerecord/lib/active_record/associations/through_association.rb
+++ b/activerecord/lib/active_record/associations/through_association.rb
@@ -15,7 +15,7 @@ module ActiveRecord
scope = super
chain[1..-1].each do |reflection|
scope = scope.merge(
- reflection.klass.scoped.with_default_scope.
+ reflection.klass.all.with_default_scope.
except(:select, :create_with, :includes, :preload, :joins, :eager_load)
)
end
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index 4af4d28b74..49ab3ab808 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -6,7 +6,7 @@ module ActiveRecord
included do
# Returns a hash of all the attributes that have been specified for serialization as
# keys and their class restriction as values.
- class_attribute :serialized_attributes
+ class_attribute :serialized_attributes, instance_writer: false
self.serialized_attributes = {}
end
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index d545e7799d..290f57659d 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -127,23 +127,17 @@ module ActiveRecord
module AutosaveAssociation
extend ActiveSupport::Concern
- ASSOCIATION_TYPES = %w{ HasOne HasMany BelongsTo HasAndBelongsToMany }
-
module AssociationBuilderExtension #:nodoc:
- def self.included(base)
- base.valid_options << :autosave
- end
-
def build
- reflection = super
model.send(:add_autosave_association_callbacks, reflection)
- reflection
+ super
end
end
included do
- ASSOCIATION_TYPES.each do |type|
- Associations::Builder.const_get(type).send(:include, AssociationBuilderExtension)
+ Associations::Builder::Association.class_eval do
+ self.valid_options << :autosave
+ include AssociationBuilderExtension
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index 3b4537aab4..be6fda95b4 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -56,7 +56,7 @@ module ActiveRecord
end
def select_all(arel, name = nil, binds = [])
- if @query_cache_enabled
+ if @query_cache_enabled && !locked?(arel)
sql = to_sql(arel, binds)
cache_sql(sql, binds) { super(sql, name, binds) }
else
@@ -65,6 +65,7 @@ module ActiveRecord
end
private
+
def cache_sql(sql, binds)
result =
if @query_cache[sql].key?(binds)
@@ -83,6 +84,10 @@ module ActiveRecord
result.collect { |row| row.dup }
end
end
+
+ def locked?(arel)
+ arel.respond_to?(:locked) && arel.locked
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 6f9f0399db..60a9eee7c7 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -31,7 +31,7 @@ module ActiveRecord
# BigDecimals need to be put in a non-normalized form and quoted.
when nil then "NULL"
when BigDecimal then value.to_s('F')
- when Numeric then value.to_s
+ when Numeric, ActiveSupport::Duration then value.to_s
when Date, Time then "'#{quoted_date(value)}'"
when Symbol then "'#{quote_string(value.to_s)}'"
when Class then "'#{value.to_s}'"
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index df78ba6c5a..f9ae1ed475 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -259,7 +259,7 @@ module ActiveRecord
end # end
EOV
end
-
+
# Adds index options to the indexes hash, keyed by column name
# This is primarily used to track indexes that need to be created after the table
#
@@ -271,7 +271,7 @@ module ActiveRecord
# Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
# <tt>:updated_at</tt> to the table.
def timestamps(*args)
- options = { :null => false }.merge(args.extract_options!)
+ options = args.extract_options!
column(:created_at, :datetime, options)
column(:updated_at, :datetime, options)
end
@@ -282,7 +282,7 @@ module ActiveRecord
index_options = options.delete(:index)
args.each do |col|
column("#{col}_id", :integer, options)
- column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
+ column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
index(polymorphic ? %w(id type).map { |t| "#{col}_#{t}" } : "#{col}_id", index_options.is_a?(Hash) ? index_options : nil) if index_options
end
end
@@ -441,17 +441,13 @@ module ActiveRecord
# Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
# <tt>references</tt> and <tt>belongs_to</tt> are acceptable.
#
- # t.references(:goat)
- # t.references(:goat, :polymorphic => true)
- # t.belongs_to(:goat)
+ # t.references(:user)
+ # t.belongs_to(:supplier, polymorphic: true)
+ #
def references(*args)
options = args.extract_options!
- polymorphic = options.delete(:polymorphic)
- index_options = options.delete(:index)
- args.each do |col|
- @base.add_column(@table_name, "#{col}_id", :integer, options)
- @base.add_column(@table_name, "#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
- @base.add_index(@table_name, polymorphic ? %w(id type).map { |t| "#{col}_#{t}" } : "#{col}_id", index_options.is_a?(Hash) ? index_options : nil) if index_options
+ args.each do |ref_name|
+ @base.add_reference(@table_name, ref_name, options)
end
end
alias :belongs_to :references
@@ -459,18 +455,16 @@ module ActiveRecord
# Removes a reference. Optionally removes a +type+ column.
# <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
#
- # t.remove_references(:goat)
- # t.remove_references(:goat, :polymorphic => true)
- # t.remove_belongs_to(:goat)
+ # t.remove_references(:user)
+ # t.remove_belongs_to(:supplier, polymorphic: true)
+ #
def remove_references(*args)
options = args.extract_options!
- polymorphic = options.delete(:polymorphic)
- args.each do |col|
- @base.remove_column(@table_name, "#{col}_id")
- @base.remove_column(@table_name, "#{col}_type") unless polymorphic.nil?
+ args.each do |ref_name|
+ @base.remove_reference(@table_name, ref_name, options)
end
end
- alias :remove_belongs_to :remove_references
+ alias :remove_belongs_to :remove_references
# Adds a column or columns of a specified type
#
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 2b0ba2f479..09f103100e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -57,7 +57,6 @@ module ActiveRecord
# Checks to see if a column exists in a given table.
#
- # === Examples
# # Check a column exists
# column_exists?(:suppliers, :name)
#
@@ -65,7 +64,10 @@ module ActiveRecord
# column_exists?(:suppliers, :name, :string)
#
# # Check a column exists with a specific definition
- # column_exists?(:suppliers, :name, :string, :limit => 100)
+ # column_exists?(:suppliers, :name, :string, limit: 100)
+ # column_exists?(:suppliers, :name, :string, default: 'default')
+ # column_exists?(:suppliers, :name, :string, null: false)
+ # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
def column_exists?(table_name, column_name, type = nil, options = {})
columns(table_name).any?{ |c| c.name == column_name.to_s &&
(!type || c.type == type) &&
@@ -202,11 +204,14 @@ module ActiveRecord
join_table_name = find_join_table_name(table_1, table_2, options)
column_options = options.delete(:column_options) || {}
- column_options.reverse_merge!({:null => false})
+ column_options.reverse_merge!(null: false)
- create_table(join_table_name, options.merge!(:id => false)) do |td|
- td.integer :"#{table_1.to_s.singularize}_id", column_options
- td.integer :"#{table_2.to_s.singularize}_id", column_options
+ t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
+
+ create_table(join_table_name, options.merge!(id: false)) do |td|
+ td.integer t1_column, column_options
+ td.integer t2_column, column_options
+ yield td if block_given?
end
end
@@ -441,6 +446,42 @@ module ActiveRecord
indexes(table_name).detect { |i| i.name == index_name }
end
+ # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
+ # <tt>add_reference</tt> and <tt>add_belongs_to</tt> are acceptable.
+ #
+ # ====== Create a user_id column
+ # add_reference(:products, :user)
+ #
+ # ====== Create a supplier_id and supplier_type columns
+ # add_belongs_to(:products, :supplier, polymorphic: true)
+ #
+ # ====== Create a supplier_id, supplier_type columns and appropriate index
+ # add_reference(:products, :supplier, polymorphic: true, index: true)
+ #
+ def add_reference(table_name, ref_name, options = {})
+ polymorphic = options.delete(:polymorphic)
+ index_options = options.delete(:index)
+ add_column(table_name, "#{ref_name}_id", :integer, options)
+ add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
+ add_index(table_name, polymorphic ? %w[id type].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : nil) if index_options
+ end
+ alias :add_belongs_to :add_reference
+
+ # Removes the reference(s). Also removes a +type+ column if one exists.
+ # <tt>remove_reference</tt>, <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
+ #
+ # ====== Remove the reference
+ # remove_reference(:products, :user, index: true)
+ #
+ # ====== Remove polymorphic reference
+ # remove_reference(:products, :supplier, polymorphic: true)
+ #
+ def remove_reference(table_name, ref_name, options = {})
+ remove_column(table_name, "#{ref_name}_id")
+ remove_column(table_name, "#{ref_name}_type") if options[:polymorphic]
+ end
+ alias :remove_belongs_to :remove_reference
+
# Returns a string of <tt>CREATE TABLE</tt> SQL statement(s) for recreating the
# entire structure of the database.
def structure_dump
@@ -449,7 +490,7 @@ module ActiveRecord
def dump_schema_information #:nodoc:
sm_table = ActiveRecord::Migrator.schema_migrations_table_name
- ActiveRecord::SchemaMigration.order('version').all.map { |sm|
+ ActiveRecord::SchemaMigration.order('version').map { |sm|
"INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');"
}.join "\n\n"
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index df4a9d5afc..2b0bc3f497 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -318,7 +318,7 @@ module ActiveRecord
select_all(sql, 'SCHEMA').map { |table|
table.delete('Table_type')
sql = "SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}"
- exec_without_stmt(sql, 'SCHEMA').first['Create Table'] + ";\n\n"
+ exec_query(sql, 'SCHEMA').first['Create Table'] + ";\n\n"
}.join
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 0b6734b010..3b0353358a 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -215,7 +215,7 @@ module ActiveRecord
def select_rows(sql, name = nil)
@connection.query_with_result = true
- rows = exec_without_stmt(sql, name).rows
+ rows = exec_query(sql, name).rows
@connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
rows
end
@@ -279,31 +279,171 @@ module ActiveRecord
end
def exec_query(sql, name = 'SQL', binds = [])
- log(sql, name, binds) do
- exec_stmt(sql, name, binds) do |cols, stmt|
- ActiveRecord::Result.new(cols, stmt.to_a) if cols
- end
+ # If the configuration sets prepared_statements:false, binds will
+ # always be empty, since the bind variables will have been already
+ # substituted and removed from binds by BindVisitor, so this will
+ # effectively disable prepared statement usage completely.
+ if binds.empty?
+ result_set, affected_rows = exec_without_stmt(sql, name)
+ else
+ result_set, affected_rows = exec_stmt(sql, name, binds)
end
+
+ yield affected_rows if block_given?
+
+ result_set
end
def last_inserted_id(result)
@connection.insert_id
end
+ class Result < ActiveRecord::Result
+ def initialize(columns, rows, column_types)
+ super(columns, rows)
+ @column_types = column_types
+ end
+ end
+
+ module Fields
+ class Type
+ def type; end
+
+ def type_cast_for_write(value)
+ value
+ end
+ end
+
+ class Identity < Type
+ def type_cast(value); value; end
+ end
+
+ class Integer < Type
+ def type_cast(value)
+ return if value.nil?
+
+ value.to_i rescue value ? 1 : 0
+ end
+ end
+
+ class Date < Type
+ def type; :date; end
+
+ def type_cast(value)
+ return if value.nil?
+
+ # FIXME: probably we can improve this since we know it is mysql
+ # specific
+ ConnectionAdapters::Column.value_to_date value
+ end
+ end
+
+ class DateTime < Type
+ def type; :datetime; end
+
+ def type_cast(value)
+ return if value.nil?
+
+ # FIXME: probably we can improve this since we know it is mysql
+ # specific
+ ConnectionAdapters::Column.string_to_time value
+ end
+ end
+
+ class Time < Type
+ def type; :time; end
+
+ def type_cast(value)
+ return if value.nil?
+
+ # FIXME: probably we can improve this since we know it is mysql
+ # specific
+ ConnectionAdapters::Column.string_to_dummy_time value
+ end
+ end
+
+ class Float < Type
+ def type; :float; end
+
+ def type_cast(value)
+ return if value.nil?
+
+ value.to_f
+ end
+ end
+
+ class Decimal < Type
+ def type_cast(value)
+ return if value.nil?
+
+ ConnectionAdapters::Column.value_to_decimal value
+ end
+ end
+
+ class Boolean < Type
+ def type_cast(value)
+ return if value.nil?
+
+ ConnectionAdapters::Column.value_to_boolean value
+ end
+ end
+
+ TYPES = {}
+
+ # Register an MySQL +type_id+ with a typcasting object in
+ # +type+.
+ def self.register_type(type_id, type)
+ TYPES[type_id] = type
+ end
+
+ def self.alias_type(new, old)
+ TYPES[new] = TYPES[old]
+ end
+
+ register_type Mysql::Field::TYPE_TINY, Fields::Boolean.new
+ register_type Mysql::Field::TYPE_LONG, Fields::Integer.new
+ alias_type Mysql::Field::TYPE_LONGLONG, Mysql::Field::TYPE_LONG
+ alias_type Mysql::Field::TYPE_NEWDECIMAL, Mysql::Field::TYPE_LONG
+
+ register_type Mysql::Field::TYPE_VAR_STRING, Fields::Identity.new
+ register_type Mysql::Field::TYPE_BLOB, Fields::Identity.new
+ register_type Mysql::Field::TYPE_DATE, Fields::Date.new
+ register_type Mysql::Field::TYPE_DATETIME, Fields::DateTime.new
+ register_type Mysql::Field::TYPE_TIME, Fields::Time.new
+ register_type Mysql::Field::TYPE_FLOAT, Fields::Float.new
+
+ Mysql::Field.constants.grep(/TYPE/).map { |class_name|
+ Mysql::Field.const_get class_name
+ }.reject { |const| TYPES.key? const }.each do |const|
+ register_type const, Fields::Identity.new
+ end
+ end
+
def exec_without_stmt(sql, name = 'SQL') # :nodoc:
# Some queries, like SHOW CREATE TABLE don't work through the prepared
# statement API. For those queries, we need to use this method. :'(
log(sql, name) do
result = @connection.query(sql)
- cols = []
- rows = []
+ affected_rows = @connection.affected_rows
if result
- cols = result.fetch_fields.map { |field| field.name }
- rows = result.to_a
+ types = {}
+ result.fetch_fields.each { |field|
+ if field.decimals > 0
+ types[field.name] = Fields::Decimal.new
+ else
+ types[field.name] = Fields::TYPES.fetch(field.type) {
+ Fields::Identity.new
+ }
+ end
+ }
+ result_set = Result.new(types.keys, result.to_a, types)
result.free
+ else
+ result_set = ActiveRecord::Result.new([], [])
end
- ActiveRecord::Result.new(cols, rows)
+
+ [result_set, affected_rows]
end
end
@@ -321,16 +461,18 @@ module ActiveRecord
alias :create :insert_sql
def exec_delete(sql, name, binds)
- log(sql, name, binds) do
- exec_stmt(sql, name, binds) do |cols, stmt|
- stmt.affected_rows
- end
+ affected_rows = 0
+
+ exec_query(sql, name, binds) do |n|
+ affected_rows = n
end
+
+ affected_rows
end
alias :exec_update :exec_delete
def begin_db_transaction #:nodoc:
- exec_without_stmt "BEGIN"
+ exec_query "BEGIN"
rescue Mysql::Error
# Transactions aren't supported
end
@@ -339,41 +481,44 @@ module ActiveRecord
def exec_stmt(sql, name, binds)
cache = {}
- if binds.empty?
- stmt = @connection.prepare(sql)
- else
- cache = @statements[sql] ||= {
- :stmt => @connection.prepare(sql)
- }
- stmt = cache[:stmt]
- end
+ log(sql, name, binds) do
+ if binds.empty?
+ stmt = @connection.prepare(sql)
+ else
+ cache = @statements[sql] ||= {
+ :stmt => @connection.prepare(sql)
+ }
+ stmt = cache[:stmt]
+ end
- begin
- stmt.execute(*binds.map { |col, val| type_cast(val, col) })
- rescue Mysql::Error => e
- # Older versions of MySQL leave the prepared statement in a bad
- # place when an error occurs. To support older mysql versions, we
- # need to close the statement and delete the statement from the
- # cache.
- stmt.close
- @statements.delete sql
- raise e
- end
+ begin
+ stmt.execute(*binds.map { |col, val| type_cast(val, col) })
+ rescue Mysql::Error => e
+ # Older versions of MySQL leave the prepared statement in a bad
+ # place when an error occurs. To support older mysql versions, we
+ # need to close the statement and delete the statement from the
+ # cache.
+ stmt.close
+ @statements.delete sql
+ raise e
+ end
- cols = nil
- if metadata = stmt.result_metadata
- cols = cache[:cols] ||= metadata.fetch_fields.map { |field|
- field.name
- }
- end
+ cols = nil
+ if metadata = stmt.result_metadata
+ cols = cache[:cols] ||= metadata.fetch_fields.map { |field|
+ field.name
+ }
+ end
- result = yield [cols, stmt]
+ result_set = ActiveRecord::Result.new(cols, stmt.to_a) if cols
+ affected_rows = stmt.affected_rows
- stmt.result_metadata.free if cols
- stmt.free_result
- stmt.close if binds.empty?
+ stmt.result_metadata.free if cols
+ stmt.free_result
+ stmt.close if binds.empty?
- result
+ [result_set, affected_rows]
+ end
end
def connect
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 7b263fd62d..71a84011bc 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -474,6 +474,7 @@ module ActiveRecord
def reconnect!
clear_cache!
@connection.reset
+ @open_transactions = 0
configure_connection
end
@@ -1227,12 +1228,19 @@ module ActiveRecord
end
# Renames a table.
+ # Also renames a table's primary key sequence if the sequence name matches the
+ # Active Record default.
#
# Example:
# rename_table('octopuses', 'octopi')
def rename_table(name, new_name)
clear_cache!
execute "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
+ pk, seq = pk_and_sequence_for(new_name)
+ if seq == "#{name}_#{pk}_seq"
+ new_seq = "#{new_name}_#{pk}_seq"
+ execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
+ end
end
# Adds a new column to the named table.
@@ -1374,7 +1382,7 @@ module ActiveRecord
UNIQUE_VIOLATION = "23505"
def translate_exception(exception, message)
- case exception.result.error_field(PGresult::PG_DIAG_SQLSTATE)
+ case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
when UNIQUE_VIOLATION
RecordNotUnique.new(message, exception)
when FOREIGN_KEY_VIOLATION
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 90f156456e..803d68187c 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -380,7 +380,7 @@ module ActiveRecord
#
# So we can avoid the method_missing hit by explicitly defining #to_ary as nil here.
#
- # See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary/
+ # See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary.html
def to_ary # :nodoc:
nil
end
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index 770083ac13..4a24024105 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -41,14 +41,26 @@ module ActiveRecord
@symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class
end
- # Returns the base AR subclass that this class descends from. If A
- # extends AR::Base, A.base_class will return A. If B descends from A
+ # Returns the class descending directly from ActiveRecord::Base (or
+ # that includes ActiveRecord::Model), or an abstract class, if any, in
+ # the inheritance hierarchy.
+ #
+ # If A extends AR::Base, A.base_class will return A. If B descends from A
# through some arbitrarily deep hierarchy, B.base_class will return A.
#
# If B < A and C < B and if A is an abstract_class then both B.base_class
# and C.base_class would return B as the answer since A is an abstract_class.
def base_class
- class_of_active_record_descendant(self)
+ unless self < Model::Tag
+ raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
+ end
+
+ sup = active_record_super
+ if sup.in?([Base, Model]) || sup.abstract_class?
+ self
+ else
+ sup.base_class
+ end
end
# Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
@@ -96,21 +108,6 @@ module ActiveRecord
protected
- # Returns the class descending directly from ActiveRecord::Base or an
- # abstract class, if any, in the inheritance hierarchy.
- def class_of_active_record_descendant(klass)
- unless klass < Model::Tag
- raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
- end
-
- sup = klass.active_record_super
- if [Base, Model].include?(klass) || [Base, Model].include?(sup) || sup.abstract_class?
- klass
- else
- class_of_active_record_descendant(sup)
- end
- end
-
# Returns the class type of the record using the current module as a prefix. So descendants of
# MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
def compute_type(type_name)
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 4ce42feb74..e0344f3c56 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -86,7 +86,7 @@ module ActiveRecord
stmt = relation.where(
relation.table[self.class.primary_key].eq(id).and(
- relation.table[lock_col].eq(quote_value(previous_lock_value))
+ relation.table[lock_col].eq(self.class.quote_value(previous_lock_value))
)
).arel.compile_update(arel_attributes_with_values_for_update(attribute_names))
diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb
index 96b62fdd61..95f4360578 100644
--- a/activerecord/lib/active_record/migration/command_recorder.rb
+++ b/activerecord/lib/active_record/migration/command_recorder.rb
@@ -51,13 +51,15 @@ module ActiveRecord
super || delegate.respond_to?(*args)
end
- [:create_table, :create_join_table, :change_table, :rename_table, :add_column, :remove_column, :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps, :change_column, :change_column_default].each do |method|
+ [:create_table, :create_join_table, :change_table, :rename_table, :add_column, :remove_column, :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps, :change_column, :change_column_default, :add_reference, :remove_reference].each do |method|
class_eval <<-EOV, __FILE__, __LINE__ + 1
def #{method}(*args) # def create_table(*args)
record(:"#{method}", args) # record(:create_table, args)
end # end
EOV
end
+ alias :add_belongs_to :add_reference
+ alias :remove_belongs_to :remove_reference
private
@@ -102,6 +104,16 @@ module ActiveRecord
[:remove_timestamps, args]
end
+ def invert_add_reference(args)
+ [:remove_reference, args]
+ end
+ alias :invert_add_belongs_to :invert_add_reference
+
+ def invert_remove_reference(args)
+ [:add_reference, args]
+ end
+ alias :invert_remove_belongs_to :invert_remove_reference
+
# Forwards any missing method call to the \target.
def method_missing(method, *args, &block)
@delegate.send(method, *args, &block)
diff --git a/activerecord/lib/active_record/migration/join_table.rb b/activerecord/lib/active_record/migration/join_table.rb
index 01a580781b..e880ae97bb 100644
--- a/activerecord/lib/active_record/migration/join_table.rb
+++ b/activerecord/lib/active_record/migration/join_table.rb
@@ -4,13 +4,11 @@ module ActiveRecord
private
def find_join_table_name(table_1, table_2, options = {})
- options.delete(:table_name) { join_table_name(table_1, table_2) }
+ options.delete(:table_name) || join_table_name(table_1, table_2)
end
def join_table_name(table_1, table_2)
- tables_names = [table_1, table_2].map(&:to_s).sort
-
- tables_names.join("_").to_sym
+ [table_1, table_2].sort.join("_").to_sym
end
end
end
diff --git a/activerecord/lib/active_record/model.rb b/activerecord/lib/active_record/model.rb
index 7b3d926d91..fb3fb643ff 100644
--- a/activerecord/lib/active_record/model.rb
+++ b/activerecord/lib/active_record/model.rb
@@ -74,9 +74,9 @@ module ActiveRecord
include Inheritance
include Scoping
include Sanitization
- include Integration
include AttributeAssignment
include ActiveModel::Conversion
+ include Integration
include Validations
include CounterCache
include Locking::Optimistic
@@ -103,7 +103,9 @@ module ActiveRecord
def abstract_class?
false
end
-
+
+ # Defines the name of the table column which will store the class name on single-table
+ # inheritance situations.
def inheritance_column
'type'
end
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index 841681e542..7febb5539f 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -351,7 +351,7 @@ module ActiveRecord
if respond_to?(method)
send(method, attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
else
- raise ArgumentError, "Cannot build association #{association_name}. Are you trying to build a polymorphic one-to-one association?"
+ raise ArgumentError, "Cannot build association `#{association_name}'. Are you trying to build a polymorphic one-to-one association?"
end
end
end
@@ -373,7 +373,7 @@ module ActiveRecord
# })
#
# Will update the name of the Person with ID 1, build a new associated
- # person with the name `John', and mark the associated Person with ID 2
+ # person with the name 'John', and mark the associated Person with ID 2
# for destruction.
#
# Also accepts an Array of attribute hashes:
diff --git a/activerecord/lib/active_record/null_relation.rb b/activerecord/lib/active_record/null_relation.rb
index aca8291d75..4c1c91e3df 100644
--- a/activerecord/lib/active_record/null_relation.rb
+++ b/activerecord/lib/active_record/null_relation.rb
@@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
module ActiveRecord
- # = Active Record Null Relation
- module NullRelation
+ module NullRelation # :nodoc:
def exec_queries
@records = []
end
diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb
index fdf17c003c..e940d357da 100644
--- a/activerecord/lib/active_record/observer.rb
+++ b/activerecord/lib/active_record/observer.rb
@@ -74,6 +74,12 @@ module ActiveRecord
#
# Observers will not be invoked unless you define these in your application configuration.
#
+ # If you are using Active Record outside Rails, activate the observers explicitly in a configuration or
+ # environment file:
+ #
+ # ActiveRecord::Base.add_observer CommentObserver.instance
+ # ActiveRecord::Base.add_observer SignupObserver.instance
+ #
# == Loading
#
# Observers register themselves in the model class they observe, since it is the class that
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index a23597be28..2830d651ba 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -167,22 +167,6 @@ module ActiveRecord
became
end
- # Updates a single attribute of an object, without calling save.
- #
- # * Validation is skipped.
- # * Callbacks are skipped.
- # * updated_at/updated_on column is not updated if that column is available.
- #
- # Raises an +ActiveRecordError+ when called on new objects, or when the +name+
- # attribute is marked as readonly.
- def update_column(name, value)
- name = name.to_s
- verify_readonly_attribute(name)
- raise ActiveRecordError, "can not update on a new record object" unless persisted?
- raw_write_attribute(name, value)
- self.class.where(self.class.primary_key => id).update_all(name => value) == 1
- end
-
# Updates the attributes of the model from the passed-in hash and saves the
# record, all wrapped in a transaction. If the object is invalid, the saving
# will fail and false will be returned.
@@ -211,6 +195,45 @@ module ActiveRecord
end
end
+ # Updates a single attribute of an object, without calling save.
+ #
+ # * Validation is skipped.
+ # * Callbacks are skipped.
+ # * updated_at/updated_on column is not updated if that column is available.
+ #
+ # Raises an +ActiveRecordError+ when called on new objects, or when the +name+
+ # attribute is marked as readonly.
+ def update_column(name, value)
+ msg = "update_column is deprecated and will be removed in 4.1. Please use update_columns. " \
+ "E.g. update_columns(foo: 'bar')"
+
+ ActiveSupport::Deprecation.warn(msg)
+
+ update_columns(name => value)
+ end
+
+ # Updates the attributes from the passed-in hash, without calling save.
+ #
+ # * Validation is skipped.
+ # * Callbacks are skipped.
+ # * updated_at/updated_on column is not updated if that column is available.
+ #
+ # Raises an +ActiveRecordError+ when called on new objects, or when at least
+ # one of the attributes is marked as readonly.
+ def update_columns(attributes)
+ raise ActiveRecordError, "can not update on a new record object" unless persisted?
+
+ attributes.each_key do |key|
+ raise ActiveRecordError, "#{key.to_s} is marked as readonly" if self.class.readonly_attributes.include?(key.to_s)
+ end
+
+ attributes.each do |k,v|
+ raw_write_attribute(k,v)
+ end
+
+ self.class.where(self.class.primary_key => id).update_all(attributes) == 1
+ end
+
# Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
# The increment is performed directly on the underlying attribute, no setter is invoked.
# Only makes sense for number-based attributes. Returns +self+.
@@ -225,7 +248,7 @@ module ActiveRecord
# Saving is not subjected to validation checks. Returns +true+ if the
# record could be saved.
def increment!(attribute, by = 1)
- increment(attribute, by).update_column(attribute, self[attribute])
+ increment(attribute, by).update_columns(attribute => self[attribute])
end
# Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
@@ -242,7 +265,7 @@ module ActiveRecord
# Saving is not subjected to validation checks. Returns +true+ if the
# record could be saved.
def decrement!(attribute, by = 1)
- decrement(attribute, by).update_column(attribute, self[attribute])
+ decrement(attribute, by).update_columns(attribute => self[attribute])
end
# Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
@@ -259,7 +282,7 @@ module ActiveRecord
# Saving is not subjected to validation checks. Returns +true+ if the
# record could be saved.
def toggle!(attribute)
- toggle(attribute).update_column(attribute, self[attribute])
+ toggle(attribute).update_columns(attribute => self[attribute])
end
# Reloads the attributes of this object from the database.
diff --git a/activerecord/lib/active_record/query_cache.rb b/activerecord/lib/active_record/query_cache.rb
index 9701898415..d64dee10fe 100644
--- a/activerecord/lib/active_record/query_cache.rb
+++ b/activerecord/lib/active_record/query_cache.rb
@@ -34,16 +34,22 @@ module ActiveRecord
response = @app.call(env)
response[2] = Rack::BodyProxy.new(response[2]) do
- ActiveRecord::Base.connection_id = connection_id
- ActiveRecord::Base.connection.clear_query_cache
- ActiveRecord::Base.connection.disable_query_cache! unless enabled
+ restore_query_cache_settings(connection_id, enabled)
end
response
rescue Exception => e
+ restore_query_cache_settings(connection_id, enabled)
+ raise e
+ end
+
+ private
+
+ def restore_query_cache_settings(connection_id, enabled)
+ ActiveRecord::Base.connection_id = connection_id
ActiveRecord::Base.connection.clear_query_cache
ActiveRecord::Base.connection.disable_query_cache! unless enabled
- raise e
end
+
end
end
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index 4d8283bcff..b8b54efe9f 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -3,15 +3,15 @@ require 'active_support/deprecation'
module ActiveRecord
module Querying
- delegate :find, :take, :take!, :first, :first!, :last, :last!, :all, :exists?, :any?, :many?, :to => :scoped
- delegate :first_or_create, :first_or_create!, :first_or_initialize, :to => :scoped
- delegate :find_by, :find_by!, :to => :scoped
- delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :scoped
- delegate :find_each, :find_in_batches, :to => :scoped
+ delegate :find, :take, :take!, :first, :first!, :last, :last!, :to_a, :exists?, :any?, :many?, :to => :all
+ delegate :first_or_create, :first_or_create!, :first_or_initialize, :to => :all
+ delegate :find_by, :find_by!, :to => :all
+ delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :all
+ delegate :find_each, :find_in_batches, :to => :all
delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins,
:where, :preload, :eager_load, :includes, :from, :lock, :readonly,
- :having, :create_with, :uniq, :references, :none, :to => :scoped
- delegate :count, :average, :minimum, :maximum, :sum, :calculate, :pluck, :ids, :to => :scoped
+ :having, :create_with, :uniq, :references, :none, :to => :all
+ delegate :count, :average, :minimum, :maximum, :sum, :calculate, :pluck, :ids, :to => :all
# Executes a custom SQL query against your database and returns all the results. The results will
# be returned as an array with columns requested encapsulated as attributes of the model you call
@@ -62,8 +62,10 @@ module ActiveRecord
#
# Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
def count_by_sql(sql)
- sql = sanitize_conditions(sql)
- connection.select_value(sql, "#{name} Count").to_i
+ logging_query_plan do
+ sql = sanitize_conditions(sql)
+ connection.select_value(sql, "#{name} Count").to_i
+ end
end
end
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 78ecb1cdc5..f5991e893c 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -2,7 +2,7 @@ require 'active_support/core_ext/object/inclusion'
require 'active_record'
db_namespace = namespace :db do
- task :load_config => :rails_env do
+ task :load_config do
ActiveRecord::Base.configurations = Rails.application.config.database_configuration
ActiveRecord::Migrator.migrations_paths = Rails.application.paths['db/migrate'].to_a
@@ -20,7 +20,7 @@ db_namespace = namespace :db do
end
desc 'Create the database from config/database.yml for the current Rails.env (use db:create:all to create all dbs in the config)'
- task :create => :load_config do
+ task :create => [:load_config, :rails_env] do
ActiveRecord::Tasks::DatabaseTasks.create_current
end
@@ -31,7 +31,7 @@ db_namespace = namespace :db do
end
desc 'Drops the database for the current Rails.env (use db:drop:all to drop all databases)'
- task :drop => :load_config do
+ task :drop => [:load_config, :rails_env] do
ActiveRecord::Tasks::DatabaseTasks.drop_current
end
@@ -88,8 +88,8 @@ db_namespace = namespace :db do
end
desc 'Display status of migrations'
- task :status => [:environment, :load_config] do
- config = ActiveRecord::Base.configurations[Rails.env || 'development']
+ task :status => [:environment, :load_config, :rails_env] do
+ config = ActiveRecord::Base.configurations[Rails.env]
ActiveRecord::Base.establish_connection(config)
unless ActiveRecord::Base.connection.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
puts 'Schema migrations table does not exist yet.'
@@ -142,12 +142,12 @@ db_namespace = namespace :db do
end
# desc "Retrieves the charset for the current environment's database"
- task :charset => [:environment, :load_config] do
+ task :charset => [:environment, :load_config, :rails_env] do
puts ActiveRecord::Tasks::DatabaseTasks.charset_current
end
# desc "Retrieves the collation for the current environment's database"
- task :collation => [:environment, :load_config] do
+ task :collation => [:environment, :load_config, :rails_env] do
begin
puts ActiveRecord::Tasks::DatabaseTasks.collation_current
rescue NoMethodError
@@ -184,7 +184,7 @@ db_namespace = namespace :db do
namespace :fixtures do
desc "Load fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
- task :load => [:environment, :load_config] do
+ task :load => [:environment, :load_config, :rails_env] do
require 'active_record/fixtures'
ActiveRecord::Base.establish_connection(Rails.env)
@@ -222,7 +222,7 @@ db_namespace = namespace :db do
namespace :schema do
desc 'Create a db/schema.rb file that can be portably used against any DB supported by AR'
- task :dump => [:environment, :load_config] do
+ task :dump => [:environment, :load_config, :rails_env] do
require 'active_record/schema_dumper'
filename = ENV['SCHEMA'] || "#{Rails.root}/db/schema.rb"
File.open(filename, "w:utf-8") do |file|
@@ -248,7 +248,7 @@ db_namespace = namespace :db do
namespace :cache do
desc 'Create a db/schema_cache.dump file.'
- task :dump => [:environment, :load_config] do
+ task :dump => [:environment, :load_config, :rails_env] do
con = ActiveRecord::Base.connection
filename = File.join(Rails.application.config.paths["db"].first, "schema_cache.dump")
@@ -277,7 +277,7 @@ db_namespace = namespace :db do
end
desc 'Dump the database structure to db/structure.sql. Specify another file with DB_STRUCTURE=db/my_structure.sql'
- task :dump => [:environment, :load_config] do
+ task :dump => [:environment, :load_config, :rails_env] do
abcs = ActiveRecord::Base.configurations
filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql")
case abcs[Rails.env]['adapter']
@@ -448,7 +448,7 @@ namespace :railties do
puts "Copied migration #{migration.basename} from #{name}"
end
- ActiveRecord::Migration.copy( ActiveRecord::Migrator.migrations_paths.first, railties,
+ ActiveRecord::Migration.copy(ActiveRecord::Migrator.migrations_paths.first, railties,
:on_skip => on_skip, :on_copy => on_copy)
end
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 0d9534acd6..dede453e38 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -20,9 +20,9 @@ module ActiveRecord
# MacroReflection class has info for the AssociationReflection
# class.
module ClassMethods
- def create_reflection(macro, name, options, active_record)
+ def create_reflection(macro, name, scope, options, active_record)
klass = options[:through] ? ThroughReflection : AssociationReflection
- reflection = klass.new(macro, name, options, active_record)
+ reflection = klass.new(macro, name, scope, options, active_record)
self.reflections = self.reflections.merge(name => reflection)
reflection
@@ -71,6 +71,8 @@ module ActiveRecord
# <tt>has_many :clients</tt> returns <tt>:has_many</tt>
attr_reader :macro
+ attr_reader :scope
+
# Returns the hash of options used for the macro.
#
# <tt>has_many :clients</tt> returns +{}+
@@ -80,9 +82,10 @@ module ActiveRecord
attr_reader :plural_name # :nodoc:
- def initialize(macro, name, options, active_record)
+ def initialize(macro, name, scope, options, active_record)
@macro = macro
@name = name
+ @scope = scope
@options = options
@active_record = active_record
@plural_name = active_record.pluralize_table_names ?
@@ -113,10 +116,6 @@ module ActiveRecord
active_record == other_aggregation.active_record
end
- def sanitized_conditions #:nodoc:
- @sanitized_conditions ||= klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
- end
-
private
def derive_class_name
name.to_s.camelize
@@ -142,7 +141,7 @@ module ActiveRecord
@klass ||= active_record.send(:compute_type, class_name)
end
- def initialize(macro, name, options, active_record)
+ def initialize(*args)
super
@collection = [:has_many, :has_and_belongs_to_many].include?(macro)
end
@@ -244,11 +243,10 @@ module ActiveRecord
false
end
- # An array of arrays of conditions. Each item in the outside array corresponds to a reflection
- # in the #chain. The inside arrays are simply conditions (and each condition may itself be
- # a hash, array, arel predicate, etc...)
- def conditions
- [[options[:conditions]].compact]
+ # An array of arrays of scopes. Each item in the outside array corresponds to a reflection
+ # in the #chain.
+ def scope_chain
+ scope ? [[scope]] : [[]]
end
alias :source_macro :macro
@@ -416,28 +414,25 @@ module ActiveRecord
# has_many :tags
# end
#
- # There may be conditions on Person.comment_tags, Article.comment_tags and/or Comment.tags,
+ # There may be scopes on Person.comment_tags, Article.comment_tags and/or Comment.tags,
# but only Comment.tags will be represented in the #chain. So this method creates an array
- # of conditions corresponding to the chain. Each item in the #conditions array corresponds
- # to an item in the #chain, and is itself an array of conditions from an arbitrary number
- # of relevant reflections, plus any :source_type or polymorphic :as constraints.
- def conditions
- @conditions ||= begin
- conditions = source_reflection.conditions.map { |c| c.dup }
+ # of scopes corresponding to the chain.
+ def scope_chain
+ @scope_chain ||= begin
+ scope_chain = source_reflection.scope_chain.map(&:dup)
- # Add to it the conditions from this reflection if necessary.
- conditions.first << options[:conditions] if options[:conditions]
+ # Add to it the scope from this reflection (if any)
+ scope_chain.first << scope if scope
- through_conditions = through_reflection.conditions
+ through_scope_chain = through_reflection.scope_chain
if options[:source_type]
- through_conditions.first << { foreign_type => options[:source_type] }
+ through_scope_chain.first <<
+ through_reflection.klass.where(foreign_type => options[:source_type])
end
# Recursively fill out the rest of the array from the through reflection
- conditions += through_conditions
-
- conditions
+ scope_chain + through_scope_chain
end
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index e268d451e0..3821c6122a 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -75,6 +75,18 @@ module ActiveRecord
binds)
end
+ # Initializes new record from relation while maintaining the current
+ # scope.
+ #
+ # Expects arguments in the same format as +Base.new+.
+ #
+ # users = User.where(name: 'DHH')
+ # user = users.new # => #<User id: nil, name: "DHH", created_at: nil, updated_at: nil>
+ #
+ # You can also pass a block to new with the new record as argument:
+ #
+ # user = users.new { |user| user.name = 'Oscar' }
+ # user.name # => Oscar
def new(*args, &block)
scoping { @klass.new(*args, &block) }
end
@@ -87,17 +99,38 @@ module ActiveRecord
alias build new
+ # Tries to create a new record with the same scoped attributes
+ # defined in the relation. Returns the initialized object if validation fails.
+ #
+ # Expects arguments in the same format as +Base.create+.
+ #
+ # ==== Examples
+ # users = User.where(name: 'Oscar')
+ # users.create # #<User id: 3, name: "oscar", ...>
+ #
+ # users.create(name: 'fxn')
+ # users.create # #<User id: 4, name: "fxn", ...>
+ #
+ # users.create { |user| user.name = 'tenderlove' }
+ # # #<User id: 5, name: "tenderlove", ...>
+ #
+ # users.create(name: nil) # validation on name
+ # # #<User id: nil, name: nil, ...>
def create(*args, &block)
scoping { @klass.create(*args, &block) }
end
+ # Similar to #create, but calls +create!+ on the base class. Raises
+ # an exception if a validation error occurs.
+ #
+ # Expects arguments in the same format as <tt>Base.create!</tt>.
def create!(*args, &block)
scoping { @klass.create!(*args, &block) }
end
# Tries to load the first record; if it fails, then <tt>create</tt> is called with the same arguments as this method.
#
- # Expects arguments in the same format as <tt>Base.create</tt>.
+ # Expects arguments in the same format as +Base.create+.
#
# ==== Examples
# # Find the first user named Penélope or create a new one.
@@ -145,12 +178,13 @@ module ActiveRecord
# are needed by the next ones when eager loading is going on.
#
# Please see further details in the
- # {Active Record Query Interface guide}[http://edgeguides.rubyonrails.org/active_record_querying.html#running-explain].
+ # {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain].
def explain
_, queries = collecting_queries_for_explain { exec_queries }
exec_explain(queries)
end
+ # Converts relation objects to Array.
def to_a
# We monitor here the entire execution rather than individual SELECTs
# because from the point of view of the user fetching the records of a
@@ -209,6 +243,7 @@ module ActiveRecord
c.respond_to?(:zero?) ? c.zero? : c.empty?
end
+ # Returns true if there are any records.
def any?
if block_given?
to_a.any? { |*block_args| yield(*block_args) }
@@ -217,6 +252,7 @@ module ActiveRecord
end
end
+ # Returns true if there is more than one record.
def many?
if block_given?
to_a.many? { |*block_args| yield(*block_args) }
@@ -227,8 +263,6 @@ module ActiveRecord
# Scope all queries to the current scope.
#
- # ==== Example
- #
# Comment.where(:post_id => 1).scoping do
# Comment.first # SELECT * FROM comments WHERE post_id = 1
# end
@@ -250,17 +284,14 @@ module ActiveRecord
# ==== Parameters
#
# * +updates+ - A string, array, or hash representing the SET part of an SQL statement.
- # * +conditions+ - A string, array, or hash representing the WHERE part of an SQL statement.
- # See conditions in the intro.
- # * +options+ - Additional options are <tt>:limit</tt> and <tt>:order</tt>, see the examples for usage.
#
# ==== Examples
#
# # Update all customers with the given attributes
- # Customer.update_all :wants_email => true
+ # Customer.update_all wants_email: true
#
# # Update all books with 'Rails' in their title
- # Book.where('title LIKE ?', '%Rails%').update_all(:author => 'David')
+ # Book.where('title LIKE ?', '%Rails%').update_all(author: 'David')
#
# # Update all books that match conditions, but limit it to 5 ordered by date
# Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(:author => 'David')
@@ -293,7 +324,7 @@ module ActiveRecord
# ==== Examples
#
# # Updates one record
- # Person.update(15, :user_name => 'Samuel', :group => 'expert')
+ # Person.update(15, user_name: 'Samuel', group: 'expert')
#
# # Updates multiple records
# people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
@@ -333,7 +364,7 @@ module ActiveRecord
# ==== Examples
#
# Person.destroy_all("last_login < '2004-04-04'")
- # Person.destroy_all(:status => "inactive")
+ # Person.destroy_all(status: "inactive")
# Person.where(:age => 0..18).destroy_all
def destroy_all(conditions = nil)
if conditions
@@ -435,6 +466,7 @@ module ActiveRecord
where(primary_key => id_or_array).delete_all
end
+ # Forces reloading of relation.
def reload
reset
to_a # force reload
@@ -448,10 +480,18 @@ module ActiveRecord
self
end
+ # Returns sql statement for the relation.
+ #
+ # Users.where(name: 'Oscar').to_sql
+ # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
def to_sql
@to_sql ||= klass.connection.to_sql(arel, bind_values.dup)
end
+ # Returns a hash of where conditions
+ #
+ # Users.where(name: 'Oscar').where_values_hash
+ # # => {:name=>"oscar"}
def where_values_hash
equalities = with_default_scope.where_values.grep(Arel::Nodes::Equality).find_all { |node|
node.left.relation.name == table_name
@@ -469,6 +509,7 @@ module ActiveRecord
@scope_for_create ||= where_values_hash.merge(create_with_value)
end
+ # Returns true if relation needs eager loading.
def eager_loading?
@should_eager_load ||=
eager_load_values.any? ||
@@ -483,6 +524,7 @@ module ActiveRecord
includes_values & joins_values
end
+ # Compares two relations for equality.
def ==(other)
case other
when Relation
@@ -506,6 +548,7 @@ module ActiveRecord
end
end
+ # Returns true if relation is blank.
def blank?
to_a.blank?
end
@@ -514,6 +557,13 @@ module ActiveRecord
@values.dup
end
+ def inspect
+ entries = to_a.take([limit_value, 11].compact.min).map!(&:inspect)
+ entries[10] = '...' if entries.size == 11
+
+ "#<#{self.class.name} [#{entries.join(', ')}]>"
+ end
+
private
def references_eager_loaded_tables?
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index fb4388d4b2..fddfb5c11e 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -9,8 +9,8 @@ module ActiveRecord
# In that case, batch processing methods allow you to work
# with the records in batches, thereby greatly reducing memory consumption.
#
- # The <tt>find_each</tt> method uses <tt>find_in_batches</tt> with a batch size of 1000 (or as
- # specified by the <tt>:batch_size</tt> option).
+ # The #find_each method uses #find_in_batches with a batch size of 1000 (or as
+ # specified by the +:batch_size+ option).
#
# Person.all.find_each do |person|
# person.do_awesome_stuff
@@ -20,7 +20,7 @@ module ActiveRecord
# person.party_all_night!
# end
#
- # You can also pass the <tt>:start</tt> option to specify
+ # You can also pass the +:start+ option to specify
# an offset to control the starting point.
def find_each(options = {})
find_in_batches(options) do |records|
@@ -29,14 +29,14 @@ module ActiveRecord
end
# Yields each batch of records that was found by the find +options+ as
- # an array. The size of each batch is set by the <tt>:batch_size</tt>
+ # an array. The size of each batch is set by the +:batch_size+
# option; the default is 1000.
#
# You can control the starting point for the batch processing by
- # supplying the <tt>:start</tt> option. This is especially useful if you
+ # supplying the +:start+ option. This is especially useful if you
# want multiple workers dealing with the same processing queue. You can
# make worker 1 handle all the records between id 0 and 10,000 and
- # worker 2 handle from 10,000 and beyond (by setting the <tt>:start</tt>
+ # worker 2 handle from 10,000 and beyond (by setting the +:start+
# option on that worker).
#
# It's not possible to set the order. That is automatically set to
@@ -67,7 +67,7 @@ module ActiveRecord
batch_size = options.delete(:batch_size) || 1000
relation = relation.reorder(batch_order).limit(batch_size)
- records = relation.where(table[primary_key].gteq(start)).all
+ records = relation.where(table[primary_key].gteq(start)).to_a
while records.any?
records_size = records.size
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index 64dda4f35a..a1c7e5b549 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -1,7 +1,7 @@
require 'active_support/core_ext/module/delegation'
module ActiveRecord
- module Delegation
+ module Delegation # :nodoc:
# Set up common delegations for performance (avoids method_missing)
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :to => :to_a
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 974cd326ef..c01aed2d8e 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -133,19 +133,6 @@ module ActiveRecord
last or raise RecordNotFound
end
- # Runs the query on the database and returns records with the used query
- # methods.
- #
- # Person.all # returns an array of objects for all the rows fetched by SELECT * FROM people
- # Person.where(["category IN (?)", categories]).limit(50).all
- # Person.where({ :friends => ["Bob", "Steve", "Fred"] }).all
- # Person.offset(10).limit(10).all
- # Person.includes([:account, :friends]).all
- # Person.group("category").all
- def all
- to_a
- end
-
# Returns +true+ if a record exists in the table that matches the +id+ or
# conditions given, or +false+ otherwise. The argument can take six forms:
#
@@ -285,7 +272,7 @@ module ActiveRecord
end
def find_some(ids)
- result = where(table[primary_key].in(ids)).all
+ result = where(table[primary_key].in(ids)).to_a
expected_size =
if limit_value && ids.size > limit_value
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index 36f98c6480..b04dd7c6a7 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -3,7 +3,7 @@ require 'active_support/core_ext/hash/keys'
module ActiveRecord
class Relation
- class HashMerger
+ class HashMerger # :nodoc:
attr_reader :relation, :hash
def initialize(relation, hash)
@@ -28,7 +28,7 @@ module ActiveRecord
end
end
- class Merger
+ class Merger # :nodoc:
attr_reader :relation, :values
def initialize(relation, other)
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 6f49548aab..a58f02098b 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -35,16 +35,39 @@ module ActiveRecord
CODE
end
- def create_with_value
+ def create_with_value # :nodoc:
@values[:create_with] || {}
end
alias extensions extending_values
+ # Specify relationships to be included in the result set. For
+ # example:
+ #
+ # users = User.includes(:address)
+ # users.each do |user|
+ # user.address.city
+ # end
+ #
+ # allows you to access the +address+ attribute of the +User+ model without
+ # firing an additional query. This will often result in a
+ # performance improvement over a simple +join+.
+ #
+ # === conditions
+ #
+ # If you want to add conditions to your included models you'll have
+ # to explicitly reference them. For example:
+ #
+ # User.includes(:posts).where('posts.name = ?', 'example')
+ #
+ # Will throw an error, but this will work:
+ #
+ # User.includes(:posts).where('posts.name = ?', 'example').references(:posts)
def includes(*args)
args.empty? ? self : spawn.includes!(*args)
end
+ # Like #includes, but modifies the relation in place.
def includes!(*args)
args.reject! {|a| a.blank? }
@@ -52,19 +75,31 @@ module ActiveRecord
self
end
+ # Forces eager loading by performing a LEFT OUTER JOIN on +args+:
+ #
+ # User.eager_load(:posts)
+ # => SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, ...
+ # FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
+ # "users"."id"
def eager_load(*args)
args.blank? ? self : spawn.eager_load!(*args)
end
+ # Like #eager_load, but modifies relation in place.
def eager_load!(*args)
self.eager_load_values += args
self
end
+ # Allows preloading of +args+, in the same way that +includes+ does:
+ #
+ # User.preload(:posts)
+ # => SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3)
def preload(*args)
args.blank? ? self : spawn.preload!(*args)
end
+ # Like #preload, but modifies relation in place.
def preload!(*args)
self.preload_values += args
self
@@ -82,6 +117,7 @@ module ActiveRecord
args.blank? ? self : spawn.references!(*args)
end
+ # Like #references, but modifies relation in place.
def references!(*args)
args.flatten!
@@ -93,7 +129,7 @@ module ActiveRecord
#
# First: takes a block so it can be used just like Array#select.
#
- # Model.scoped.select { |m| m.field == value }
+ # Model.all.select { |m| m.field == value }
#
# This will build an array of objects from the database for the scope,
# converting them into an array and iterating through them using Array#select.
@@ -126,15 +162,29 @@ module ActiveRecord
end
end
+ # Like #select, but modifies relation in place.
def select!(value)
self.select_values += Array.wrap(value)
self
end
+ # Allows to specify a group attribute:
+ #
+ # User.group(:name)
+ # => SELECT "users".* FROM "users" GROUP BY name
+ #
+ # Returns an array with distinct records based on the +group+ attribute:
+ #
+ # User.select([:id, :name])
+ # => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">
+ #
+ # User.group(:name)
+ # => [#<User id: 3, name: "Foo", ...>, #<User id: 2, name: "Oscar", ...>]
def group(*args)
args.blank? ? self : spawn.group!(*args)
end
+ # Like #group, but modifies relation in place.
def group!(*args)
args.flatten!
@@ -142,10 +192,21 @@ module ActiveRecord
self
end
+ # Allows to specify an order attribute:
+ #
+ # User.order('name')
+ # => SELECT "users".* FROM "users" ORDER BY name
+ #
+ # User.order('name DESC')
+ # => SELECT "users".* FROM "users" ORDER BY name DESC
+ #
+ # User.order('name DESC, email')
+ # => SELECT "users".* FROM "users" ORDER BY name DESC, email
def order(*args)
args.blank? ? self : spawn.order!(*args)
end
+ # Like #order, but modifies relation in place.
def order!(*args)
args.flatten!
@@ -170,6 +231,7 @@ module ActiveRecord
args.blank? ? self : spawn.reorder!(*args)
end
+ # Like #reorder, but modifies relation in place.
def reorder!(*args)
args.flatten!
@@ -178,10 +240,15 @@ module ActiveRecord
self
end
+ # Performs a joins on +args+:
+ #
+ # User.joins(:posts)
+ # => SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
def joins(*args)
args.compact.blank? ? self : spawn.joins!(*args)
end
+ # Like #joins, but modifies relation in place.
def joins!(*args)
args.flatten!
@@ -301,10 +368,15 @@ module ActiveRecord
self
end
+ # Allows to specify a HAVING clause. Note that you can't use HAVING
+ # without also specifying a GROUP clause.
+ #
+ # Order.having('SUM(price) > 30').group('user_id')
def having(opts, *rest)
opts.blank? ? self : spawn.having!(opts, *rest)
end
+ # Like #having, but modifies relation in place.
def having!(opts, *rest)
references!(PredicateBuilder.references(opts)) if Hash === opts
@@ -321,6 +393,7 @@ module ActiveRecord
spawn.limit!(value)
end
+ # Like #limit, but modifies relation in place.
def limit!(value)
self.limit_value = value
self
@@ -337,15 +410,19 @@ module ActiveRecord
spawn.offset!(value)
end
+ # Like #offset, but modifies relation in place.
def offset!(value)
self.offset_value = value
self
end
+ # Specifies locking settings (default to +true+). For more information
+ # on locking, please see +ActiveRecord::Locking+.
def lock(locks = true)
spawn.lock!(locks)
end
+ # Like #lock, but modifies relation in place.
def lock!(locks = true)
case locks
when String, TrueClass, NilClass
@@ -358,11 +435,11 @@ module ActiveRecord
end
# Returns a chainable relation with zero records, specifically an
- # instance of the NullRelation class.
+ # instance of the <tt>ActiveRecord::NullRelation</tt> class.
#
- # The returned NullRelation inherits from Relation and implements the
- # Null Object pattern so it is an object with defined null behavior:
- # it always returns an empty array of records and does not query the database.
+ # The returned <tt>ActiveRecord::NullRelation</tt> inherits from Relation and implements the
+ # Null Object pattern. It is an object with defined null behavior and always returns an empty
+ # array of records without quering the database.
#
# Any subsequent condition chained to the returned relation will continue
# generating an empty relation and will not fire any query to the database.
@@ -387,22 +464,47 @@ module ActiveRecord
# end
#
def none
- scoped.extending(NullRelation)
+ extending(NullRelation)
end
+ # Sets readonly attributes for the returned relation. If value is
+ # true (default), attempting to update a record will result in an error.
+ #
+ # users = User.readonly
+ # users.first.save
+ # => ActiveRecord::ReadOnlyRecord: ActiveRecord::ReadOnlyRecord
def readonly(value = true)
spawn.readonly!(value)
end
+ # Like #readonly, but modifies relation in place.
def readonly!(value = true)
self.readonly_value = value
self
end
+ # Sets attributes to be used when creating new records from a
+ # relation object.
+ #
+ # users = User.where(name: 'Oscar')
+ # users.new.name # => 'Oscar'
+ #
+ # users = users.create_with(name: 'DHH')
+ # users.new.name # => 'DHH'
+ #
+ # You can pass +nil+ to +create_with+ to reset attributes:
+ #
+ # users = users.create_with(nil)
+ # users.new.name # => 'Oscar'
def create_with(value)
spawn.create_with!(value)
end
+ # Like #create_with but modifies the relation in place. Raises
+ # +ImmutableRelation+ if the relation has already been loaded.
+ #
+ # users = User.all.create_with!(name: 'Oscar')
+ # users.new.name # => 'Oscar'
def create_with!(value)
self.create_with_value = value ? create_with_value.merge(value) : {}
self
@@ -425,6 +527,7 @@ module ActiveRecord
spawn.from!(value, subquery_name)
end
+ # Like #from, but modifies relation in place.
def from!(value, subquery_name = nil)
self.from_value = [value, subquery_name]
self
@@ -444,6 +547,7 @@ module ActiveRecord
spawn.uniq!(value)
end
+ # Like #uniq, but modifies relation in place.
def uniq!(value = true)
self.uniq_value = value
self
@@ -462,16 +566,16 @@ module ActiveRecord
# end
# end
#
- # scope = Model.scoped.extending(Pagination)
+ # scope = Model.all.extending(Pagination)
# scope.page(params[:page])
#
# You can also pass a list of modules:
#
- # scope = Model.scoped.extending(Pagination, SomethingElse)
+ # scope = Model.all.extending(Pagination, SomethingElse)
#
# === Using a block
#
- # scope = Model.scoped.extending do
+ # scope = Model.all.extending do
# def page(number)
# # pagination code goes here
# end
@@ -480,7 +584,7 @@ module ActiveRecord
#
# You can also use a block and a module list:
#
- # scope = Model.scoped.extending(Pagination) do
+ # scope = Model.all.extending(Pagination) do
# def per_page(number)
# # pagination code goes here
# end
@@ -493,10 +597,11 @@ module ActiveRecord
end
end
+ # Like #extending, but modifies relation in place.
def extending!(*modules, &block)
modules << Module.new(&block) if block_given?
- self.extending_values = modules.flatten
+ self.extending_values += modules.flatten
extend(*extending_values) if extending_values.any?
self
@@ -509,17 +614,20 @@ module ActiveRecord
spawn.reverse_order!
end
+ # Like #reverse_order, but modifies relation in place.
def reverse_order!
self.reverse_order_value = !reverse_order_value
self
end
+ # Returns the Arel object associated with the relation.
def arel
@arel ||= with_default_scope.build_arel
end
+ # Like #arel, but ignores the default scope of the model.
def build_arel
- arel = table.from table
+ arel = Arel::SelectManager.new(table.engine, table)
build_joins(arel, joins_values) unless joins_values.empty?
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index 80d087a9ea..d21f02cd5f 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -34,6 +34,7 @@ module ActiveRecord
end
end
+ # Like #merge, but applies changes in place.
def merge!(other)
klass = other.is_a?(Hash) ? Relation::HashMerger : Relation::Merger
klass.new(self, other).merge
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index 46f6c283e3..ca767cc704 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -148,15 +148,8 @@ module ActiveRecord
end
# TODO: Deprecate this
- def quoted_id #:nodoc:
- quote_value(id, column_for_attribute(self.class.primary_key))
- end
-
- private
-
- # Quote strings appropriately for SQL statements.
- def quote_value(value, column = nil)
- self.class.connection.quote(value, column)
+ def quoted_id
+ self.class.quote_value(id, column_for_attribute(self.class.primary_key))
end
end
end
diff --git a/activerecord/lib/active_record/schema_migration.rb b/activerecord/lib/active_record/schema_migration.rb
index 236ec563d2..ca22154c84 100644
--- a/activerecord/lib/active_record/schema_migration.rb
+++ b/activerecord/lib/active_record/schema_migration.rb
@@ -7,7 +7,11 @@ module ActiveRecord
attr_accessible :version
def self.table_name
- Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix
+ "#{Base.table_name_prefix}schema_migrations#{Base.table_name_suffix}"
+ end
+
+ def self.index_name
+ "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
end
def self.create_table
@@ -15,14 +19,13 @@ module ActiveRecord
connection.create_table(table_name, :id => false) do |t|
t.column :version, :string, :null => false
end
- connection.add_index table_name, :version, :unique => true,
- :name => "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
+ connection.add_index table_name, :version, :unique => true, :name => index_name
end
end
def self.drop_table
if connection.table_exists?(table_name)
- connection.remove_index table_name, :name => "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
+ connection.remove_index table_name, :name => index_name
connection.drop_table(table_name)
end
end
diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb
index af51c803a7..b35fec7920 100644
--- a/activerecord/lib/active_record/scoping/default.rb
+++ b/activerecord/lib/active_record/scoping/default.rb
@@ -31,14 +31,14 @@ module ActiveRecord
# Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
# }
#
- # It is recommended that you use the block form of unscoped because chaining
- # unscoped with <tt>scope</tt> does not work. Assuming that
+ # It is recommended that you use the block form of unscoped because
+ # chaining unscoped with <tt>scope</tt> does not work. Assuming that
# <tt>published</tt> is a <tt>scope</tt>, the following two statements
- # are equal: the default_scope is applied on both.
+ # are equal: the <tt>default_scope</tt> is applied on both.
#
# Post.unscoped.published
# Post.published
- def unscoped #:nodoc:
+ def unscoped
block_given? ? relation.scoping { yield } : relation
end
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
index 2af476c1ba..4cd86cefe1 100644
--- a/activerecord/lib/active_record/scoping/named.rb
+++ b/activerecord/lib/active_record/scoping/named.rb
@@ -12,33 +12,26 @@ module ActiveRecord
extend ActiveSupport::Concern
module ClassMethods
- # Returns an anonymous \scope.
+ # Returns an <tt>ActiveRecord::Relation</tt> scope object.
#
- # posts = Post.scoped
+ # posts = Post.all
# posts.size # Fires "select count(*) from posts" and returns the count
# posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
#
- # fruits = Fruit.scoped
+ # fruits = Fruit.all
# fruits = fruits.where(:color => 'red') if options[:red_only]
# fruits = fruits.limit(10) if limited?
#
- # Anonymous \scopes tend to be useful when procedurally generating complex
- # queries, where passing intermediate values (\scopes) around as first-class
- # objects is convenient.
- #
# You can define a \scope that applies to all finders using
# ActiveRecord::Base.default_scope.
- def scoped(options = nil)
+ def all
if current_scope
- scope = current_scope.clone
+ current_scope.clone
else
scope = relation
scope.default_scoped = true
scope
end
-
- scope.merge!(options) if options
- scope
end
##
@@ -189,7 +182,7 @@ module ActiveRecord
singleton_class.send(:define_method, name) do |*args|
options = body.respond_to?(:call) ? unscoped { body.call(*args) } : body
- relation = scoped.merge(options)
+ relation = all.merge(options)
extension ? relation.extending(extension) : relation
end
diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb
index 5a256b040b..d2d3106721 100644
--- a/activerecord/lib/active_record/session_store.rb
+++ b/activerecord/lib/active_record/session_store.rb
@@ -7,7 +7,7 @@ module ActiveRecord
#
# The default assumes a +sessions+ tables with columns:
# +id+ (numeric primary key),
- # +session_id+ (text, or longtext if your session data exceeds 65K), and
+ # +session_id+ (string, usually varchar; maximum length is 255), and
# +data+ (text or longtext; careful if your session data exceeds 65KB).
#
# The +session_id+ column should always be indexed for speedy lookups.
@@ -218,7 +218,7 @@ module ActiveRecord
# Look up a session by id and unmarshal its data if found.
def find_by_session_id(session_id)
- if record = connection.select_one("SELECT * FROM #{@@table_name} WHERE #{@@session_id_column}=#{connection.quote(session_id.to_s)}")
+ if record = connection.select_one("SELECT #{connection.quote_column_name(data_column)} AS data FROM #{@@table_name} WHERE #{connection.quote_column_name(@@session_id_column)}=#{connection.quote(session_id.to_s)}")
new(:session_id => session_id, :marshaled_data => record['data'])
end
end
diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb
index d13491502e..81576e7cd3 100644
--- a/activerecord/lib/active_record/store.rb
+++ b/activerecord/lib/active_record/store.rb
@@ -43,7 +43,7 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- class_attribute :stored_attributes
+ class_attribute :stored_attributes, instance_writer: false
self.stored_attributes = {}
end
@@ -57,14 +57,15 @@ module ActiveRecord
keys = keys.flatten
keys.each do |key|
define_method("#{key}=") do |value|
- initialize_store_attribute(store_attribute)
- send(store_attribute)[key] = value
- send :"#{store_attribute}_will_change!"
+ attribute = initialize_store_attribute(store_attribute)
+ if value != attribute[key]
+ attribute[key] = value
+ send :"#{store_attribute}_will_change!"
+ end
end
define_method(key) do
- initialize_store_attribute(store_attribute)
- send(store_attribute)[key]
+ initialize_store_attribute(store_attribute)[key]
end
end
@@ -74,16 +75,12 @@ module ActiveRecord
private
def initialize_store_attribute(store_attribute)
- case attribute = send(store_attribute)
- when ActiveSupport::HashWithIndifferentAccess
- # Already initialized. Do nothing.
- when Hash
- # Initialized as a Hash. Convert to indifferent access.
- send :"#{store_attribute}=", attribute.with_indifferent_access
- else
- # Uninitialized. Set to an indifferent hash.
- send :"#{store_attribute}=", ActiveSupport::HashWithIndifferentAccess.new
+ attribute = send(store_attribute)
+ unless attribute.is_a?(HashWithIndifferentAccess)
+ attribute = IndifferentCoder.as_indifferent_hash(attribute)
+ send :"#{store_attribute}=", attribute
end
+ attribute
end
class IndifferentCoder
@@ -106,7 +103,7 @@ module ActiveRecord
def self.as_indifferent_hash(obj)
case obj
- when ActiveSupport::HashWithIndifferentAccess
+ when HashWithIndifferentAccess
obj
when Hash
obj.with_indifferent_access
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index f1241502f5..fb3dfc2730 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -3,13 +3,17 @@ module ActiveRecord
module DatabaseTasks # :nodoc:
extend self
- TASKS_PATTERNS = {
- /mysql/ => ActiveRecord::Tasks::MySQLDatabaseTasks,
- /postgresql/ => ActiveRecord::Tasks::PostgreSQLDatabaseTasks,
- /sqlite/ => ActiveRecord::Tasks::SQLiteDatabaseTasks
- }
LOCAL_HOSTS = ['127.0.0.1', 'localhost']
+ def register_task(pattern, task)
+ @tasks ||= {}
+ @tasks[pattern] = task
+ end
+
+ register_task(/mysql/, ActiveRecord::Tasks::MySQLDatabaseTasks)
+ register_task(/postgresql/, ActiveRecord::Tasks::PostgreSQLDatabaseTasks)
+ register_task(/sqlite/, ActiveRecord::Tasks::SQLiteDatabaseTasks)
+
def create(*arguments)
configuration = arguments.first
class_for_adapter(configuration['adapter']).new(*arguments).create
@@ -84,8 +88,8 @@ module ActiveRecord
private
def class_for_adapter(adapter)
- key = TASKS_PATTERNS.keys.detect { |pattern| adapter[pattern] }
- TASKS_PATTERNS[key]
+ key = @tasks.keys.detect { |pattern| adapter[pattern] }
+ @tasks[key]
end
def each_current_configuration(environment)
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index 9e4b588ac2..5a24135f8e 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -26,7 +26,7 @@ module ActiveRecord
relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.send(:id))) if record.persisted?
Array(options[:scope]).each do |scope_item|
- scope_value = record.send(scope_item)
+ scope_value = record.read_attribute(scope_item)
reflection = record.class.reflect_on_association(scope_item)
if reflection
scope_value = record.send(reflection.foreign_key)
diff --git a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
index 1509e34473..f6a432c6e5 100644
--- a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
@@ -11,15 +11,36 @@ module ActiveRecord
end
protected
- attr_reader :migration_action
+ attr_reader :migration_action, :join_tables
- def set_local_assigns!
- if file_name =~ /^(add|remove)_.*_(?:to|from)_(.*)/
- @migration_action = $1
- @table_name = $2.pluralize
+ def set_local_assigns!
+ case file_name
+ when /^(add|remove)_.*_(?:to|from)_(.*)/
+ @migration_action = $1
+ @table_name = $2.pluralize
+ when /join_table/
+ if attributes.length == 2
+ @migration_action = 'join'
+ @join_tables = attributes.map(&:plural_name)
+
+ set_index_names
end
end
+ end
+
+ def set_index_names
+ attributes.each_with_index do |attr, i|
+ attr.index_name = [attr, attributes[i - 1]].map{ |a| index_name_for(a) }
+ end
+ end
+ def index_name_for(attribute)
+ if attribute.foreign_key?
+ attribute.name
+ else
+ attribute.name.singularize.foreign_key
+ end.to_sym
+ end
end
end
end
diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
index b1a0f83b5f..d5c07aecd3 100644
--- a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
+++ b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
@@ -2,30 +2,50 @@ class <%= migration_class_name %> < ActiveRecord::Migration
<%- if migration_action == 'add' -%>
def change
<% attributes.each do |attribute| -%>
+ <%- if attribute.reference? -%>
+ add_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %>
+ <%- else -%>
add_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %>
<%- if attribute.has_index? -%>
add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
<%- end -%>
+ <%- end -%>
<%- end -%>
end
+<%- elsif migration_action == 'join' -%>
+ def change
+ create_join_table :<%= join_tables.first %>, :<%= join_tables.second %> do |t|
+ <%- attributes.each do |attribute| -%>
+ <%= '# ' unless attribute.has_index? -%>t.index <%= attribute.index_name %><%= attribute.inject_index_options %>
+ <%- end -%>
+ end
+ end
<%- else -%>
def up
<% attributes.each do |attribute| -%>
- <%- if migration_action -%>
- <%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %>
+<%- if migration_action -%>
+ <%- if attribute.reference? -%>
+ remove_reference :<%= table_name %>, :<%= attribute.name %><%= ', polymorphic: true' if attribute.polymorphic? %>
+ <%- else -%>
+ remove_column :<%= table_name %>, :<%= attribute.name %>
<%- end -%>
<%- end -%>
+<%- end -%>
end
def down
<% attributes.reverse.each do |attribute| -%>
- <%- if migration_action -%>
+<%- if migration_action -%>
+ <%- if attribute.reference? -%>
+ add_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %>
+ <%- else -%>
add_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %>
<%- if attribute.has_index? -%>
add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
<%- end -%>
<%- end -%>
<%- end -%>
+<%- end -%>
end
<%- end -%>
end
diff --git a/activerecord/lib/rails/generators/active_record/model/templates/module.rb b/activerecord/lib/rails/generators/active_record/model/templates/module.rb
index fca2908080..a3bf1c37b6 100644
--- a/activerecord/lib/rails/generators/active_record/model/templates/module.rb
+++ b/activerecord/lib/rails/generators/active_record/model/templates/module.rb
@@ -1,7 +1,7 @@
<% module_namespacing do -%>
module <%= class_path.map(&:camelize).join('::') %>
def self.table_name_prefix
- '<%= class_path.join('_') %>_'
+ '<%= namespaced? ? namespaced_class_path.join('_') : class_path.join('_') %>_'
end
end
<% end -%>
diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb
index 5e1c52c9ba..c3f82bc63d 100644
--- a/activerecord/test/cases/adapters/mysql/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql/connection_test.rb
@@ -70,11 +70,14 @@ class MysqlConnectionTest < ActiveRecord::TestCase
assert_equal %w{ id data }, result.columns
@connection.exec_query('INSERT INTO ex (id, data) VALUES (1, "foo")')
+
+ # if there are no bind parameters, it will return a string (due to
+ # the libmysql api)
result = @connection.exec_query('SELECT id, data FROM ex')
assert_equal 1, result.rows.length
assert_equal 2, result.columns.length
- assert_equal [[1, 'foo']], result.rows
+ assert_equal [['1', 'foo']], result.rows
end
def test_exec_with_binds
diff --git a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
index 475a292f85..ddfe42b375 100644
--- a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
@@ -26,7 +26,9 @@ module ActiveRecord
result = @conn.exec_query('SELECT number FROM ex WHERE number = 10')
assert_equal 1, result.rows.length
- assert_equal 10, result.rows.last.last
+ # if there are no bind parameters, it will return a string (due to
+ # the libmysql api)
+ assert_equal '10', result.rows.last.last
end
def test_exec_insert_string
diff --git a/activerecord/test/cases/adapters/mysql/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb
index 8e3eeac81e..aff971a955 100644
--- a/activerecord/test/cases/adapters/mysql/reserved_word_test.rb
+++ b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb
@@ -130,7 +130,7 @@ class MysqlReservedWordTest < ActiveRecord::TestCase
end
def test_associations_work_with_reserved_words
- assert_nothing_raised { Select.scoped(:includes => [:groups]).all }
+ assert_nothing_raised { Select.all.merge!(:includes => [:groups]).to_a }
end
#the following functions were added to DRY test cases
diff --git a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
index 5c2c113c78..9fd07f014e 100644
--- a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
@@ -130,7 +130,7 @@ class MysqlReservedWordTest < ActiveRecord::TestCase
end
def test_associations_work_with_reserved_words
- assert_nothing_raised { Select.scoped(:includes => [:groups]).all }
+ assert_nothing_raised { Select.all.merge!(:includes => [:groups]).to_a }
end
#the following functions were added to DRY test cases
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index f823ce33d8..202f7e27c5 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -81,5 +81,77 @@ module ActiveRecord
assert_equal 'SCHEMA', @connection.logged[0][1]
end
+ def test_reconnection_after_simulated_disconnection_with_verify
+ assert @connection.active?
+ original_connection_pid = @connection.query('select pg_backend_pid()')
+
+ # Fail with bad connection after next query attempt.
+ connection_class = class << @connection ; self ; end
+ connection_class.class_eval <<-CODE
+ def query_fake(*args)
+ if @called ||= false
+ @connection.stubs(:status).returns(PCconn::CONNECTION_BAD)
+ raise PGError
+ else
+ @called = true
+ @connection.unstub(:status)
+ query_unfake(*args)
+ end
+ end
+
+ alias query_unfake query
+ alias query query_fake
+ CODE
+
+ begin
+ @connection.verify!
+ new_connection_pid = @connection.query('select pg_backend_pid()')
+ ensure
+ connection_class.class_eval <<-CODE
+ alias query query_unfake
+ undef query_fake
+ CODE
+ end
+
+ assert_equal original_connection_pid, new_connection_pid, "Should have a new underlying connection pid"
+ end
+
+ # Must have with_manual_interventions set to true for this
+ # test to run.
+ # When prompted, restart the PostgreSQL server with the
+ # "-m fast" option or kill the individual connection assuming
+ # you know the incantation to do that.
+ # To restart PostgreSQL 9.1 on OS X, installed via MacPorts, ...
+ # sudo su postgres -c "pg_ctl restart -D /opt/local/var/db/postgresql91/defaultdb/ -m fast"
+ def test_reconnection_after_actual_disconnection_with_verify
+ skip "with_manual_interventions is false in configuration" unless ARTest.config['with_manual_interventions']
+
+ original_connection_pid = @connection.query('select pg_backend_pid()')
+
+ # Sanity check.
+ assert @connection.active?
+
+ puts 'Kill the connection now (e.g. by restarting the PostgreSQL ' +
+ 'server with the "-m fast" option) and then press enter.'
+ $stdin.gets
+
+ @connection.verify!
+
+ assert @connection.active?
+
+ # If we get no exception here, then either we re-connected successfully, or
+ # we never actually got disconnected.
+ new_connection_pid = @connection.query('select pg_backend_pid()')
+
+ assert_not_equal original_connection_pid, new_connection_pid,
+ "umm -- looks like you didn't break the connection, because we're still " +
+ "successfully querying with the same connection pid."
+
+ # Repair all fixture connections so other tests won't break.
+ @fixture_connections.each do |c|
+ c.verify!
+ end
+ end
+
end
end
diff --git a/activerecord/test/cases/associations/association_scope_test.rb b/activerecord/test/cases/associations/association_scope_test.rb
new file mode 100644
index 0000000000..d38648202e
--- /dev/null
+++ b/activerecord/test/cases/associations/association_scope_test.rb
@@ -0,0 +1,15 @@
+require 'cases/helper'
+require 'models/post'
+require 'models/author'
+
+module ActiveRecord
+ module Associations
+ class AssociationScopeTest < ActiveRecord::TestCase
+ test 'does not duplicate conditions' do
+ association_scope = AssociationScope.new(Author.new.association(:welcome_posts))
+ wheres = association_scope.scope.where_values.map(&:right)
+ assert_equal wheres.uniq, wheres
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 2c7a240915..ec7e4f5fb7 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -73,14 +73,14 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_eager_loading_with_primary_key
Firm.create("name" => "Apple")
Client.create("name" => "Citibank", :firm_name => "Apple")
- citibank_result = Client.scoped(:where => {:name => "Citibank"}, :includes => :firm_with_primary_key).first
+ citibank_result = Client.all.merge!(:where => {:name => "Citibank"}, :includes => :firm_with_primary_key).first
assert citibank_result.association_cache.key?(:firm_with_primary_key)
end
def test_eager_loading_with_primary_key_as_symbol
Firm.create("name" => "Apple")
Client.create("name" => "Citibank", :firm_name => "Apple")
- citibank_result = Client.scoped(:where => {:name => "Citibank"}, :includes => :firm_with_primary_key_symbols).first
+ citibank_result = Client.all.merge!(:where => {:name => "Citibank"}, :includes => :firm_with_primary_key_symbols).first
assert citibank_result.association_cache.key?(:firm_with_primary_key_symbols)
end
@@ -181,8 +181,8 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_with_select
- assert_equal Company.find(2).firm_with_select.attributes.size, 1
- assert_equal Company.scoped(:includes => :firm_with_select ).find(2).firm_with_select.attributes.size, 1
+ assert_equal 1, Company.find(2).firm_with_select.attributes.size
+ assert_equal 1, Company.all.merge!(:includes => :firm_with_select ).find(2).firm_with_select.attributes.size
end
def test_belongs_to_counter
@@ -298,12 +298,12 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal 1, Topic.find(topic.id)[:replies_count]
end
- def test_belongs_to_counter_when_update_column
+ def test_belongs_to_counter_when_update_columns
topic = Topic.create!(:title => "37s")
topic.replies.create!(:title => "re: 37s", :content => "rails")
assert_equal 1, Topic.find(topic.id)[:replies_count]
- topic.update_column(:content, "rails is wonderfull")
+ topic.update_columns(content: "rails is wonderfull")
assert_equal 1, Topic.find(topic.id)[:replies_count]
end
@@ -334,7 +334,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_new_record_with_foreign_key_but_no_object
c = Client.new("firm_id" => 1)
# sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
- assert_equal Firm.scoped(:order => "id").first, c.firm_with_basic_id
+ assert_equal Firm.all.merge!(:order => "id").first, c.firm_with_basic_id
end
def test_setting_foreign_key_after_nil_target_loaded
@@ -396,7 +396,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_association_assignment_sticks
post = Post.first
- author1, author2 = Author.scoped(:limit => 2).all
+ author1, author2 = Author.all.merge!(:limit => 2).to_a
assert_not_nil author1
assert_not_nil author2
@@ -498,14 +498,14 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_nothing_raised do
Account.find(@account.id).save!
- Account.scoped(:includes => :firm).find(@account.id).save!
+ Account.all.merge!(:includes => :firm).find(@account.id).save!
end
@account.firm.delete
assert_nothing_raised do
Account.find(@account.id).save!
- Account.scoped(:includes => :firm).find(@account.id).save!
+ Account.all.merge!(:includes => :firm).find(@account.id).save!
end
end
diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
index 01f7f18397..80bca7f63e 100644
--- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
+++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
@@ -16,7 +16,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
:categorizations, :people, :categories, :edges, :vertices
def test_eager_association_loading_with_cascaded_two_levels
- authors = Author.scoped(:includes=>{:posts=>:comments}, :order=>"authors.id").all
+ authors = Author.all.merge!(:includes=>{:posts=>:comments}, :order=>"authors.id").to_a
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
assert_equal 3, authors[1].posts.size
@@ -24,7 +24,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_cascaded_two_levels_and_one_level
- authors = Author.scoped(:includes=>[{:posts=>:comments}, :categorizations], :order=>"authors.id").all
+ authors = Author.all.merge!(:includes=>[{:posts=>:comments}, :categorizations], :order=>"authors.id").to_a
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
assert_equal 3, authors[1].posts.size
@@ -35,16 +35,16 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
def test_eager_association_loading_with_hmt_does_not_table_name_collide_when_joining_associations
assert_nothing_raised do
- Author.joins(:posts).eager_load(:comments).where(:posts => {:taggings_count => 1}).all
+ Author.joins(:posts).eager_load(:comments).where(:posts => {:taggings_count => 1}).to_a
end
- authors = Author.joins(:posts).eager_load(:comments).where(:posts => {:taggings_count => 1}).all
+ authors = Author.joins(:posts).eager_load(:comments).where(:posts => {:taggings_count => 1}).to_a
assert_equal 1, assert_no_queries { authors.size }
assert_equal 10, assert_no_queries { authors[0].comments.size }
end
def test_eager_association_loading_grafts_stashed_associations_to_correct_parent
assert_nothing_raised do
- Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').order('people.id').all
+ Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').order('people.id').to_a
end
assert_equal people(:michael), Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').order('people.id').first
end
@@ -54,9 +54,9 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
assert_nothing_raised do
assert_equal 4, categories.count
- assert_equal 4, categories.all.count
+ assert_equal 4, categories.to_a.count
assert_equal 3, categories.count(:distinct => true)
- assert_equal 3, categories.all.uniq.size # Must uniq since instantiating with inner joins will get dupes
+ assert_equal 3, categories.to_a.uniq.size # Must uniq since instantiating with inner joins will get dupes
end
end
@@ -64,7 +64,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
categories = Category.includes(:categorizations).includes(:categorizations => :author).where("categorizations.id is not null").references(:categorizations)
assert_nothing_raised do
assert_equal 3, categories.count
- assert_equal 3, categories.all.size
+ assert_equal 3, categories.to_a.size
end
end
@@ -72,7 +72,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
categories = Category.includes(:categorizations => :author).includes(:categorizations => :post).where("posts.id is not null").references(:posts)
assert_nothing_raised do
assert_equal 3, categories.count
- assert_equal 3, categories.all.size
+ assert_equal 3, categories.to_a.size
end
end
@@ -80,11 +80,11 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
authors = Author.joins(:special_posts).includes([:posts, :categorizations])
assert_nothing_raised { authors.count }
- assert_queries(3) { authors.all }
+ assert_queries(3) { authors.to_a }
end
def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations
- authors = Author.scoped(:includes=>{:posts=>[:comments, :categorizations]}, :order=>"authors.id").all
+ authors = Author.all.merge!(:includes=>{:posts=>[:comments, :categorizations]}, :order=>"authors.id").to_a
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
assert_equal 3, authors[1].posts.size
@@ -92,7 +92,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_cascaded_two_levels_and_self_table_reference
- authors = Author.scoped(:includes=>{:posts=>[:comments, :author]}, :order=>"authors.id").all
+ authors = Author.all.merge!(:includes=>{:posts=>[:comments, :author]}, :order=>"authors.id").to_a
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
assert_equal authors(:david).name, authors[0].name
@@ -100,13 +100,13 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_cascaded_two_levels_with_condition
- authors = Author.scoped(:includes=>{:posts=>:comments}, :where=>"authors.id=1", :order=>"authors.id").all
+ authors = Author.all.merge!(:includes=>{:posts=>:comments}, :where=>"authors.id=1", :order=>"authors.id").to_a
assert_equal 1, authors.size
assert_equal 5, authors[0].posts.size
end
def test_eager_association_loading_with_cascaded_three_levels_by_ping_pong
- firms = Firm.scoped(:includes=>{:account=>{:firm=>:account}}, :order=>"companies.id").all
+ firms = Firm.all.merge!(:includes=>{:account=>{:firm=>:account}}, :order=>"companies.id").to_a
assert_equal 2, firms.size
assert_equal firms.first.account, firms.first.account.firm.account
assert_equal companies(:first_firm).account, assert_no_queries { firms.first.account.firm.account }
@@ -114,7 +114,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_has_many_sti
- topics = Topic.scoped(:includes => :replies, :order => 'topics.id').all
+ topics = Topic.all.merge!(:includes => :replies, :order => 'topics.id').to_a
first, second, = topics(:first).replies.size, topics(:second).replies.size
assert_no_queries do
assert_equal first, topics[0].replies.size
@@ -127,7 +127,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
silly.parent_id = 1
assert silly.save
- topics = Topic.scoped(:includes => :replies, :order => ['topics.id', 'replies_topics.id']).all
+ topics = Topic.all.merge!(:includes => :replies, :order => ['topics.id', 'replies_topics.id']).to_a
assert_no_queries do
assert_equal 2, topics[0].replies.size
assert_equal 0, topics[1].replies.size
@@ -135,14 +135,14 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_belongs_to_sti
- replies = Reply.scoped(:includes => :topic, :order => 'topics.id').all
+ replies = Reply.all.merge!(:includes => :topic, :order => 'topics.id').to_a
assert replies.include?(topics(:second))
assert !replies.include?(topics(:first))
assert_equal topics(:first), assert_no_queries { replies.first.topic }
end
def test_eager_association_loading_with_multiple_stis_and_order
- author = Author.scoped(:includes => { :posts => [ :special_comments , :very_special_comment ] }, :order => ['authors.name', 'comments.body', 'very_special_comments_posts.body'], :where => 'posts.id = 4').first
+ author = Author.all.merge!(:includes => { :posts => [ :special_comments , :very_special_comment ] }, :order => ['authors.name', 'comments.body', 'very_special_comments_posts.body'], :where => 'posts.id = 4').first
assert_equal authors(:david), author
assert_no_queries do
author.posts.first.special_comments
@@ -151,7 +151,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_of_stis_with_multiple_references
- authors = Author.scoped(:includes => { :posts => { :special_comments => { :post => [ :special_comments, :very_special_comment ] } } }, :order => 'comments.body, very_special_comments_posts.body', :where => 'posts.id = 4').all
+ authors = Author.all.merge!(:includes => { :posts => { :special_comments => { :post => [ :special_comments, :very_special_comment ] } } }, :order => 'comments.body, very_special_comments_posts.body', :where => 'posts.id = 4').to_a
assert_equal [authors(:david)], authors
assert_no_queries do
authors.first.posts.first.special_comments.first.post.special_comments
@@ -160,7 +160,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_where_first_level_returns_nil
- authors = Author.scoped(:includes => {:post_about_thinking => :comments}, :order => 'authors.id DESC').all
+ authors = Author.all.merge!(:includes => {:post_about_thinking => :comments}, :order => 'authors.id DESC').to_a
assert_equal [authors(:bob), authors(:mary), authors(:david)], authors
assert_no_queries do
authors[2].post_about_thinking.comments.first
@@ -168,12 +168,12 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_recursive_cascading_four_levels_has_many_through
- source = Vertex.scoped(:includes=>{:sinks=>{:sinks=>{:sinks=>:sinks}}}, :order => 'vertices.id').first
+ source = Vertex.all.merge!(:includes=>{:sinks=>{:sinks=>{:sinks=>:sinks}}}, :order => 'vertices.id').first
assert_equal vertices(:vertex_4), assert_no_queries { source.sinks.first.sinks.first.sinks.first }
end
def test_eager_association_loading_with_recursive_cascading_four_levels_has_and_belongs_to_many
- sink = Vertex.scoped(:includes=>{:sources=>{:sources=>{:sources=>:sources}}}, :order => 'vertices.id DESC').first
+ sink = Vertex.all.merge!(:includes=>{:sources=>{:sources=>{:sources=>:sources}}}, :order => 'vertices.id DESC').first
assert_equal vertices(:vertex_1), assert_no_queries { sink.sources.first.sources.first.sources.first.sources.first }
end
end
diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
index bb0d6bc70b..5ff117eaa0 100644
--- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb
+++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
@@ -92,7 +92,7 @@ class EagerLoadPolyAssocsTest < ActiveRecord::TestCase
end
def test_include_query
- res = ShapeExpression.scoped(:includes => [ :shape, { :paint => :non_poly } ]).all
+ res = ShapeExpression.all.merge!(:includes => [ :shape, { :paint => :non_poly } ]).to_a
assert_equal NUM_SHAPE_EXPRESSIONS, res.size
assert_queries(0) do
res.each do |se|
@@ -122,7 +122,7 @@ class EagerLoadNestedIncludeWithMissingDataTest < ActiveRecord::TestCase
assert_nothing_raised do
# @davey_mcdave doesn't have any author_favorites
includes = {:posts => :comments, :categorizations => :category, :author_favorites => :favorite_author }
- Author.scoped(:includes => includes, :where => {:authors => {:name => @davey_mcdave.name}}, :order => 'categories.name').to_a
+ Author.all.merge!(:includes => includes, :where => {:authors => {:name => @davey_mcdave.name}}, :order => 'categories.name').to_a
end
end
end
diff --git a/activerecord/test/cases/associations/eager_singularization_test.rb b/activerecord/test/cases/associations/eager_singularization_test.rb
index 5805e71249..634f6b63ba 100644
--- a/activerecord/test/cases/associations/eager_singularization_test.rb
+++ b/activerecord/test/cases/associations/eager_singularization_test.rb
@@ -103,43 +103,43 @@ class EagerSingularizationTest < ActiveRecord::TestCase
def test_eager_no_extra_singularization_belongs_to
return unless @have_tables
assert_nothing_raised do
- Virus.scoped(:includes => :octopus).all
+ Virus.all.merge!(:includes => :octopus).to_a
end
end
def test_eager_no_extra_singularization_has_one
return unless @have_tables
assert_nothing_raised do
- Octopus.scoped(:includes => :virus).all
+ Octopus.all.merge!(:includes => :virus).to_a
end
end
def test_eager_no_extra_singularization_has_many
return unless @have_tables
assert_nothing_raised do
- Bus.scoped(:includes => :passes).all
+ Bus.all.merge!(:includes => :passes).to_a
end
end
def test_eager_no_extra_singularization_has_and_belongs_to_many
return unless @have_tables
assert_nothing_raised do
- Crisis.scoped(:includes => :messes).all
- Mess.scoped(:includes => :crises).all
+ Crisis.all.merge!(:includes => :messes).to_a
+ Mess.all.merge!(:includes => :crises).to_a
end
end
def test_eager_no_extra_singularization_has_many_through_belongs_to
return unless @have_tables
assert_nothing_raised do
- Crisis.scoped(:includes => :successes).all
+ Crisis.all.merge!(:includes => :successes).to_a
end
end
def test_eager_no_extra_singularization_has_many_through_has_many
return unless @have_tables
assert_nothing_raised do
- Crisis.scoped(:includes => :compresses).all
+ Crisis.all.merge!(:includes => :compresses).to_a
end
end
end
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 31b11ee334..da4eeb3844 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -30,42 +30,42 @@ class EagerAssociationTest < ActiveRecord::TestCase
:developers, :projects, :developers_projects, :members, :memberships, :clubs, :sponsors
def test_eager_with_has_one_through_join_model_with_conditions_on_the_through
- member = Member.scoped(:includes => :favourite_club).find(members(:some_other_guy).id)
+ member = Member.all.merge!(:includes => :favourite_club).find(members(:some_other_guy).id)
assert_nil member.favourite_club
end
def test_loading_with_one_association
- posts = Post.scoped(:includes => :comments).all
+ posts = Post.all.merge!(:includes => :comments).to_a
post = posts.find { |p| p.id == 1 }
assert_equal 2, post.comments.size
assert post.comments.include?(comments(:greetings))
- post = Post.scoped(:includes => :comments, :where => "posts.title = 'Welcome to the weblog'").first
+ post = Post.all.merge!(:includes => :comments, :where => "posts.title = 'Welcome to the weblog'").first
assert_equal 2, post.comments.size
assert post.comments.include?(comments(:greetings))
- posts = Post.scoped(:includes => :last_comment).all
+ posts = Post.all.merge!(:includes => :last_comment).to_a
post = posts.find { |p| p.id == 1 }
assert_equal Post.find(1).last_comment, post.last_comment
end
def test_loading_with_one_association_with_non_preload
- posts = Post.scoped(:includes => :last_comment, :order => 'comments.id DESC').all
+ posts = Post.all.merge!(:includes => :last_comment, :order => 'comments.id DESC').to_a
post = posts.find { |p| p.id == 1 }
assert_equal Post.find(1).last_comment, post.last_comment
end
def test_loading_conditions_with_or
- posts = authors(:david).posts.references(:comments).scoped(
+ posts = authors(:david).posts.references(:comments).merge(
:includes => :comments,
:where => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE} = 'SpecialComment'"
- ).all
+ ).to_a
assert_nil posts.detect { |p| p.author_id != authors(:david).id },
"expected to find only david's posts"
end
def test_with_ordering
- list = Post.scoped(:includes => :comments, :order => "posts.id DESC").all
+ list = Post.all.merge!(:includes => :comments, :order => "posts.id DESC").to_a
[:other_by_mary, :other_by_bob, :misc_by_mary, :misc_by_bob, :eager_other,
:sti_habtm, :sti_post_and_comments, :sti_comments, :authorless, :thinking, :welcome
].each_with_index do |post, index|
@@ -79,14 +79,14 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_loading_with_multiple_associations
- posts = Post.scoped(:includes => [ :comments, :author, :categories ], :order => "posts.id").all
+ posts = Post.all.merge!(:includes => [ :comments, :author, :categories ], :order => "posts.id").to_a
assert_equal 2, posts.first.comments.size
assert_equal 2, posts.first.categories.size
assert posts.first.comments.include?(comments(:greetings))
end
def test_duplicate_middle_objects
- comments = Comment.scoped(:where => 'post_id = 1', :includes => [:post => :author]).all
+ comments = Comment.all.merge!(:where => 'post_id = 1', :includes => [:post => :author]).to_a
assert_no_queries do
comments.each {|comment| comment.post.author.name}
end
@@ -94,25 +94,25 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_preloading_has_many_in_multiple_queries_with_more_ids_than_database_can_handle
Post.connection.expects(:in_clause_length).at_least_once.returns(5)
- posts = Post.scoped(:includes=>:comments).all
+ posts = Post.all.merge!(:includes=>:comments).to_a
assert_equal 11, posts.size
end
def test_preloading_has_many_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle
Post.connection.expects(:in_clause_length).at_least_once.returns(nil)
- posts = Post.scoped(:includes=>:comments).all
+ posts = Post.all.merge!(:includes=>:comments).to_a
assert_equal 11, posts.size
end
def test_preloading_habtm_in_multiple_queries_with_more_ids_than_database_can_handle
Post.connection.expects(:in_clause_length).at_least_once.returns(5)
- posts = Post.scoped(:includes=>:categories).all
+ posts = Post.all.merge!(:includes=>:categories).to_a
assert_equal 11, posts.size
end
def test_preloading_habtm_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle
Post.connection.expects(:in_clause_length).at_least_once.returns(nil)
- posts = Post.scoped(:includes=>:categories).all
+ posts = Post.all.merge!(:includes=>:categories).to_a
assert_equal 11, posts.size
end
@@ -149,8 +149,8 @@ class EagerAssociationTest < ActiveRecord::TestCase
popular_post.readers.create!(:person => people(:michael))
popular_post.readers.create!(:person => people(:david))
- readers = Reader.scoped(:where => ["post_id = ?", popular_post.id],
- :includes => {:post => :comments}).all
+ readers = Reader.all.merge!(:where => ["post_id = ?", popular_post.id],
+ :includes => {:post => :comments}).to_a
readers.each do |reader|
assert_equal [comment], reader.post.comments
end
@@ -162,8 +162,8 @@ class EagerAssociationTest < ActiveRecord::TestCase
car_post.categories << categories(:technology)
comment = car_post.comments.create!(:body => "hmm")
- categories = Category.scoped(:where => { 'posts.id' => car_post.id },
- :includes => {:posts => :comments}).all
+ categories = Category.all.merge!(:where => { 'posts.id' => car_post.id },
+ :includes => {:posts => :comments}).to_a
categories.each do |category|
assert_equal [comment], category.posts[0].comments
end
@@ -181,7 +181,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_finding_with_includes_on_has_many_association_with_same_include_includes_only_once
author_id = authors(:david).id
- author = assert_queries(3) { Author.scoped(:includes => {:posts_with_comments => :comments}).find(author_id) } # find the author, then find the posts, then find the comments
+ author = assert_queries(3) { Author.all.merge!(:includes => {:posts_with_comments => :comments}).find(author_id) } # find the author, then find the posts, then find the comments
author.posts_with_comments.each do |post_with_comments|
assert_equal post_with_comments.comments.length, post_with_comments.comments.count
assert_nil post_with_comments.comments.to_a.uniq!
@@ -192,7 +192,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
author = authors(:david)
post = author.post_about_thinking_with_last_comment
last_comment = post.last_comment
- author = assert_queries(3) { Author.scoped(:includes => {:post_about_thinking_with_last_comment => :last_comment}).find(author.id)} # find the author, then find the posts, then find the comments
+ author = assert_queries(3) { Author.all.merge!(:includes => {:post_about_thinking_with_last_comment => :last_comment}).find(author.id)} # find the author, then find the posts, then find the comments
assert_no_queries do
assert_equal post, author.post_about_thinking_with_last_comment
assert_equal last_comment, author.post_about_thinking_with_last_comment.last_comment
@@ -203,7 +203,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
post = posts(:welcome)
author = post.author
author_address = author.author_address
- post = assert_queries(3) { Post.scoped(:includes => {:author_with_address => :author_address}).find(post.id) } # find the post, then find the author, then find the address
+ post = assert_queries(3) { Post.all.merge!(:includes => {:author_with_address => :author_address}).find(post.id) } # find the post, then find the author, then find the address
assert_no_queries do
assert_equal author, post.author_with_address
assert_equal author_address, post.author_with_address.author_address
@@ -213,7 +213,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_finding_with_includes_on_null_belongs_to_association_with_same_include_includes_only_once
post = posts(:welcome)
post.update_attributes!(:author => nil)
- post = assert_queries(1) { Post.scoped(:includes => {:author_with_address => :author_address}).find(post.id) } # find the post, then find the author which is null so no query for the author or address
+ post = assert_queries(1) { Post.all.merge!(:includes => {:author_with_address => :author_address}).find(post.id) } # find the post, then find the author which is null so no query for the author or address
assert_no_queries do
assert_equal nil, post.author_with_address
end
@@ -222,56 +222,56 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_finding_with_includes_on_null_belongs_to_polymorphic_association
sponsor = sponsors(:moustache_club_sponsor_for_groucho)
sponsor.update_attributes!(:sponsorable => nil)
- sponsor = assert_queries(1) { Sponsor.scoped(:includes => :sponsorable).find(sponsor.id) }
+ sponsor = assert_queries(1) { Sponsor.all.merge!(:includes => :sponsorable).find(sponsor.id) }
assert_no_queries do
assert_equal nil, sponsor.sponsorable
end
end
def test_loading_from_an_association
- posts = authors(:david).posts.scoped(:includes => :comments, :order => "posts.id").all
+ posts = authors(:david).posts.merge(:includes => :comments, :order => "posts.id").to_a
assert_equal 2, posts.first.comments.size
end
def test_loading_from_an_association_that_has_a_hash_of_conditions
assert_nothing_raised do
- Author.scoped(:includes => :hello_posts_with_hash_conditions).all
+ Author.all.merge!(:includes => :hello_posts_with_hash_conditions).to_a
end
- assert !Author.scoped(:includes => :hello_posts_with_hash_conditions).find(authors(:david).id).hello_posts.empty?
+ assert !Author.all.merge!(:includes => :hello_posts_with_hash_conditions).find(authors(:david).id).hello_posts.empty?
end
def test_loading_with_no_associations
- assert_nil Post.scoped(:includes => :author).find(posts(:authorless).id).author
+ assert_nil Post.all.merge!(:includes => :author).find(posts(:authorless).id).author
end
def test_nested_loading_with_no_associations
assert_nothing_raised do
- Post.scoped(:includes => {:author => :author_addresss}).find(posts(:authorless).id)
+ Post.all.merge!(:includes => {:author => :author_addresss}).find(posts(:authorless).id)
end
end
def test_nested_loading_through_has_one_association
- aa = AuthorAddress.scoped(:includes => {:author => :posts}).find(author_addresses(:david_address).id)
+ aa = AuthorAddress.all.merge!(:includes => {:author => :posts}).find(author_addresses(:david_address).id)
assert_equal aa.author.posts.count, aa.author.posts.length
end
def test_nested_loading_through_has_one_association_with_order
- aa = AuthorAddress.scoped(:includes => {:author => :posts}, :order => 'author_addresses.id').find(author_addresses(:david_address).id)
+ aa = AuthorAddress.all.merge!(:includes => {:author => :posts}, :order => 'author_addresses.id').find(author_addresses(:david_address).id)
assert_equal aa.author.posts.count, aa.author.posts.length
end
def test_nested_loading_through_has_one_association_with_order_on_association
- aa = AuthorAddress.scoped(:includes => {:author => :posts}, :order => 'authors.id').find(author_addresses(:david_address).id)
+ aa = AuthorAddress.all.merge!(:includes => {:author => :posts}, :order => 'authors.id').find(author_addresses(:david_address).id)
assert_equal aa.author.posts.count, aa.author.posts.length
end
def test_nested_loading_through_has_one_association_with_order_on_nested_association
- aa = AuthorAddress.scoped(:includes => {:author => :posts}, :order => 'posts.id').find(author_addresses(:david_address).id)
+ aa = AuthorAddress.all.merge!(:includes => {:author => :posts}, :order => 'posts.id').find(author_addresses(:david_address).id)
assert_equal aa.author.posts.count, aa.author.posts.length
end
def test_nested_loading_through_has_one_association_with_conditions
- aa = AuthorAddress.references(:author_addresses).scoped(
+ aa = AuthorAddress.references(:author_addresses).merge(
:includes => {:author => :posts},
:where => "author_addresses.id > 0"
).find author_addresses(:david_address).id
@@ -279,7 +279,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_nested_loading_through_has_one_association_with_conditions_on_association
- aa = AuthorAddress.references(:authors).scoped(
+ aa = AuthorAddress.references(:authors).merge(
:includes => {:author => :posts},
:where => "authors.id > 0"
).find author_addresses(:david_address).id
@@ -287,7 +287,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_nested_loading_through_has_one_association_with_conditions_on_nested_association
- aa = AuthorAddress.references(:posts).scoped(
+ aa = AuthorAddress.references(:posts).merge(
:includes => {:author => :posts},
:where => "posts.id > 0"
).find author_addresses(:david_address).id
@@ -295,12 +295,12 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_belongs_to_and_foreign_keys
- pets = Pet.scoped(:includes => :owner).all
+ pets = Pet.all.merge!(:includes => :owner).to_a
assert_equal 3, pets.length
end
def test_eager_association_loading_with_belongs_to
- comments = Comment.scoped(:includes => :post).all
+ comments = Comment.all.merge!(:includes => :post).to_a
assert_equal 11, comments.length
titles = comments.map { |c| c.post.title }
assert titles.include?(posts(:welcome).title)
@@ -308,31 +308,31 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_belongs_to_and_limit
- comments = Comment.scoped(:includes => :post, :limit => 5, :order => 'comments.id').all
+ comments = Comment.all.merge!(:includes => :post, :limit => 5, :order => 'comments.id').to_a
assert_equal 5, comments.length
assert_equal [1,2,3,5,6], comments.collect { |c| c.id }
end
def test_eager_association_loading_with_belongs_to_and_limit_and_conditions
- comments = Comment.scoped(:includes => :post, :where => 'post_id = 4', :limit => 3, :order => 'comments.id').all
+ comments = Comment.all.merge!(:includes => :post, :where => 'post_id = 4', :limit => 3, :order => 'comments.id').to_a
assert_equal 3, comments.length
assert_equal [5,6,7], comments.collect { |c| c.id }
end
def test_eager_association_loading_with_belongs_to_and_limit_and_offset
- comments = Comment.scoped(:includes => :post, :limit => 3, :offset => 2, :order => 'comments.id').all
+ comments = Comment.all.merge!(:includes => :post, :limit => 3, :offset => 2, :order => 'comments.id').to_a
assert_equal 3, comments.length
assert_equal [3,5,6], comments.collect { |c| c.id }
end
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions
- comments = Comment.scoped(:includes => :post, :where => 'post_id = 4', :limit => 3, :offset => 1, :order => 'comments.id').all
+ comments = Comment.all.merge!(:includes => :post, :where => 'post_id = 4', :limit => 3, :offset => 1, :order => 'comments.id').to_a
assert_equal 3, comments.length
assert_equal [6,7,8], comments.collect { |c| c.id }
end
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions_array
- comments = Comment.scoped(:includes => :post, :where => ['post_id = ?',4], :limit => 3, :offset => 1, :order => 'comments.id').all
+ comments = Comment.all.merge!(:includes => :post, :where => ['post_id = ?',4], :limit => 3, :offset => 1, :order => 'comments.id').to_a
assert_equal 3, comments.length
assert_equal [6,7,8], comments.collect { |c| c.id }
end
@@ -340,7 +340,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_association_loading_with_belongs_to_and_conditions_string_with_unquoted_table_name
assert_nothing_raised do
ActiveSupport::Deprecation.silence do
- Comment.scoped(:includes => :post, :where => ['posts.id = ?',4]).all
+ Comment.all.merge!(:includes => :post, :where => ['posts.id = ?',4]).to_a
end
end
end
@@ -348,7 +348,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_association_loading_with_belongs_to_and_conditions_hash
comments = []
assert_nothing_raised do
- comments = Comment.scoped(:includes => :post, :where => {:posts => {:id => 4}}, :limit => 3, :order => 'comments.id').all
+ comments = Comment.all.merge!(:includes => :post, :where => {:posts => {:id => 4}}, :limit => 3, :order => 'comments.id').to_a
end
assert_equal 3, comments.length
assert_equal [5,6,7], comments.collect { |c| c.id }
@@ -361,14 +361,14 @@ class EagerAssociationTest < ActiveRecord::TestCase
quoted_posts_id= Comment.connection.quote_table_name('posts') + '.' + Comment.connection.quote_column_name('id')
assert_nothing_raised do
ActiveSupport::Deprecation.silence do
- Comment.scoped(:includes => :post, :where => ["#{quoted_posts_id} = ?",4]).all
+ Comment.all.merge!(:includes => :post, :where => ["#{quoted_posts_id} = ?",4]).to_a
end
end
end
def test_eager_association_loading_with_belongs_to_and_order_string_with_unquoted_table_name
assert_nothing_raised do
- Comment.scoped(:includes => :post, :order => 'posts.id').all
+ Comment.all.merge!(:includes => :post, :order => 'posts.id').to_a
end
end
@@ -376,25 +376,25 @@ class EagerAssociationTest < ActiveRecord::TestCase
quoted_posts_id= Comment.connection.quote_table_name('posts') + '.' + Comment.connection.quote_column_name('id')
assert_nothing_raised do
ActiveSupport::Deprecation.silence do
- Comment.scoped(:includes => :post, :order => quoted_posts_id).all
+ Comment.all.merge!(:includes => :post, :order => quoted_posts_id).to_a
end
end
end
def test_eager_association_loading_with_belongs_to_and_limit_and_multiple_associations
- posts = Post.scoped(:includes => [:author, :very_special_comment], :limit => 1, :order => 'posts.id').all
+ posts = Post.all.merge!(:includes => [:author, :very_special_comment], :limit => 1, :order => 'posts.id').to_a
assert_equal 1, posts.length
assert_equal [1], posts.collect { |p| p.id }
end
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_multiple_associations
- posts = Post.scoped(:includes => [:author, :very_special_comment], :limit => 1, :offset => 1, :order => 'posts.id').all
+ posts = Post.all.merge!(:includes => [:author, :very_special_comment], :limit => 1, :offset => 1, :order => 'posts.id').to_a
assert_equal 1, posts.length
assert_equal [2], posts.collect { |p| p.id }
end
def test_eager_association_loading_with_belongs_to_inferred_foreign_key_from_association_name
- author_favorite = AuthorFavorite.scoped(:includes => :favorite_author).first
+ author_favorite = AuthorFavorite.all.merge!(:includes => :favorite_author).first
assert_equal authors(:mary), assert_no_queries { author_favorite.favorite_author }
end
@@ -405,26 +405,26 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_load_has_one_quotes_table_and_column_names
- michael = Person.scoped(:includes => :favourite_reference).find(people(:michael))
+ michael = Person.all.merge!(:includes => :favourite_reference).find(people(:michael))
references(:michael_unicyclist)
assert_no_queries{ assert_equal references(:michael_unicyclist), michael.favourite_reference}
end
def test_eager_load_has_many_quotes_table_and_column_names
- michael = Person.scoped(:includes => :references).find(people(:michael))
+ michael = Person.all.merge!(:includes => :references).find(people(:michael))
references(:michael_magician,:michael_unicyclist)
assert_no_queries{ assert_equal references(:michael_magician,:michael_unicyclist), michael.references.sort_by(&:id) }
end
def test_eager_load_has_many_through_quotes_table_and_column_names
- michael = Person.scoped(:includes => :jobs).find(people(:michael))
+ michael = Person.all.merge!(:includes => :jobs).find(people(:michael))
jobs(:magician, :unicyclist)
assert_no_queries{ assert_equal jobs(:unicyclist, :magician), michael.jobs.sort_by(&:id) }
end
def test_eager_load_has_many_with_string_keys
subscriptions = subscriptions(:webster_awdr, :webster_rfr)
- subscriber =Subscriber.scoped(:includes => :subscriptions).find(subscribers(:second).id)
+ subscriber =Subscriber.all.merge!(:includes => :subscriptions).find(subscribers(:second).id)
assert_equal subscriptions, subscriber.subscriptions.sort_by(&:id)
end
@@ -442,25 +442,25 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_load_has_many_through_with_string_keys
books = books(:awdr, :rfr)
- subscriber = Subscriber.scoped(:includes => :books).find(subscribers(:second).id)
+ subscriber = Subscriber.all.merge!(:includes => :books).find(subscribers(:second).id)
assert_equal books, subscriber.books.sort_by(&:id)
end
def test_eager_load_belongs_to_with_string_keys
subscriber = subscribers(:second)
- subscription = Subscription.scoped(:includes => :subscriber).find(subscriptions(:webster_awdr).id)
+ subscription = Subscription.all.merge!(:includes => :subscriber).find(subscriptions(:webster_awdr).id)
assert_equal subscriber, subscription.subscriber
end
def test_eager_association_loading_with_explicit_join
- posts = Post.scoped(:includes => :comments, :joins => "INNER JOIN authors ON posts.author_id = authors.id AND authors.name = 'Mary'", :limit => 1, :order => 'author_id').all
+ posts = Post.all.merge!(:includes => :comments, :joins => "INNER JOIN authors ON posts.author_id = authors.id AND authors.name = 'Mary'", :limit => 1, :order => 'author_id').to_a
assert_equal 1, posts.length
end
def test_eager_with_has_many_through
- posts_with_comments = people(:michael).posts.scoped(:includes => :comments, :order => 'posts.id').all
- posts_with_author = people(:michael).posts.scoped(:includes => :author, :order => 'posts.id').all
- posts_with_comments_and_author = people(:michael).posts.scoped(:includes => [ :comments, :author ], :order => 'posts.id').all
+ posts_with_comments = people(:michael).posts.merge(:includes => :comments, :order => 'posts.id').to_a
+ posts_with_author = people(:michael).posts.merge(:includes => :author, :order => 'posts.id').to_a
+ posts_with_comments_and_author = people(:michael).posts.merge(:includes => [ :comments, :author ], :order => 'posts.id').to_a
assert_equal 2, posts_with_comments.inject(0) { |sum, post| sum += post.comments.size }
assert_equal authors(:david), assert_no_queries { posts_with_author.first.author }
assert_equal authors(:david), assert_no_queries { posts_with_comments_and_author.first.author }
@@ -471,32 +471,32 @@ class EagerAssociationTest < ActiveRecord::TestCase
Post.create!(:author => author, :title => "TITLE", :body => "BODY")
author.author_favorites.create(:favorite_author_id => 1)
author.author_favorites.create(:favorite_author_id => 2)
- posts_with_author_favorites = author.posts.scoped(:includes => :author_favorites).all
+ posts_with_author_favorites = author.posts.merge(:includes => :author_favorites).to_a
assert_no_queries { posts_with_author_favorites.first.author_favorites.first.author_id }
end
def test_eager_with_has_many_through_an_sti_join_model
- author = Author.scoped(:includes => :special_post_comments, :order => 'authors.id').first
+ author = Author.all.merge!(:includes => :special_post_comments, :order => 'authors.id').first
assert_equal [comments(:does_it_hurt)], assert_no_queries { author.special_post_comments }
end
def test_eager_with_has_many_through_an_sti_join_model_with_conditions_on_both
- author = Author.scoped(:includes => :special_nonexistant_post_comments, :order => 'authors.id').first
+ author = Author.all.merge!(:includes => :special_nonexistant_post_comments, :order => 'authors.id').first
assert_equal [], author.special_nonexistant_post_comments
end
def test_eager_with_has_many_through_join_model_with_conditions
- assert_equal Author.scoped(:includes => :hello_post_comments,
+ assert_equal Author.all.merge!(:includes => :hello_post_comments,
:order => 'authors.id').first.hello_post_comments.sort_by(&:id),
- Author.scoped(:order => 'authors.id').first.hello_post_comments.sort_by(&:id)
+ Author.all.merge!(:order => 'authors.id').first.hello_post_comments.sort_by(&:id)
end
def test_eager_with_has_many_through_join_model_with_conditions_on_top_level
- assert_equal comments(:more_greetings), Author.scoped(:includes => :comments_with_order_and_conditions).find(authors(:david).id).comments_with_order_and_conditions.first
+ assert_equal comments(:more_greetings), Author.all.merge!(:includes => :comments_with_order_and_conditions).find(authors(:david).id).comments_with_order_and_conditions.first
end
def test_eager_with_has_many_through_join_model_with_include
- author_comments = Author.scoped(:includes => :comments_with_include).find(authors(:david).id).comments_with_include.to_a
+ author_comments = Author.all.merge!(:includes => :comments_with_include).find(authors(:david).id).comments_with_include.to_a
assert_no_queries do
author_comments.first.post.title
end
@@ -504,7 +504,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_has_many_through_with_conditions_join_model_with_include
post_tags = Post.find(posts(:welcome).id).misc_tags
- eager_post_tags = Post.scoped(:includes => :misc_tags).find(1).misc_tags
+ eager_post_tags = Post.all.merge!(:includes => :misc_tags).find(1).misc_tags
assert_equal post_tags, eager_post_tags
end
@@ -515,16 +515,16 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_has_many_and_limit
- posts = Post.scoped(:order => 'posts.id asc', :includes => [ :author, :comments ], :limit => 2).all
+ posts = Post.all.merge!(:order => 'posts.id asc', :includes => [ :author, :comments ], :limit => 2).to_a
assert_equal 2, posts.size
assert_equal 3, posts.inject(0) { |sum, post| sum += post.comments.size }
end
def test_eager_with_has_many_and_limit_and_conditions
if current_adapter?(:OpenBaseAdapter)
- posts = Post.scoped(:includes => [ :author, :comments ], :limit => 2, :where => "FETCHBLOB(posts.body) = 'hello'", :order => "posts.id").all
+ posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :where => "FETCHBLOB(posts.body) = 'hello'", :order => "posts.id").to_a
else
- posts = Post.scoped(:includes => [ :author, :comments ], :limit => 2, :where => "posts.body = 'hello'", :order => "posts.id").all
+ posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :where => "posts.body = 'hello'", :order => "posts.id").to_a
end
assert_equal 2, posts.size
assert_equal [4,5], posts.collect { |p| p.id }
@@ -532,9 +532,9 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_has_many_and_limit_and_conditions_array
if current_adapter?(:OpenBaseAdapter)
- posts = Post.scoped(:includes => [ :author, :comments ], :limit => 2, :where => [ "FETCHBLOB(posts.body) = ?", 'hello' ], :order => "posts.id").all
+ posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :where => [ "FETCHBLOB(posts.body) = ?", 'hello' ], :order => "posts.id").to_a
else
- posts = Post.scoped(:includes => [ :author, :comments ], :limit => 2, :where => [ "posts.body = ?", 'hello' ], :order => "posts.id").all
+ posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :where => [ "posts.body = ?", 'hello' ], :order => "posts.id").to_a
end
assert_equal 2, posts.size
assert_equal [4,5], posts.collect { |p| p.id }
@@ -542,7 +542,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_has_many_and_limit_and_conditions_array_on_the_eagers
posts = ActiveSupport::Deprecation.silence do
- Post.scoped(:includes => [ :author, :comments ], :limit => 2, :where => [ "authors.name = ?", 'David' ]).all
+ Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :where => [ "authors.name = ?", 'David' ]).to_a
end
assert_equal 2, posts.size
@@ -553,34 +553,34 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_has_many_and_limit_and_high_offset
- posts = Post.scoped(:includes => [ :author, :comments ], :limit => 2, :offset => 10, :where => { 'authors.name' => 'David' }).all
+ posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :offset => 10, :where => { 'authors.name' => 'David' }).to_a
assert_equal 0, posts.size
end
def test_eager_with_has_many_and_limit_and_high_offset_and_multiple_array_conditions
assert_queries(1) do
posts = Post.references(:authors, :comments).
- scoped(:includes => [ :author, :comments ], :limit => 2, :offset => 10,
- :where => [ "authors.name = ? and comments.body = ?", 'David', 'go crazy' ]).all
+ merge(:includes => [ :author, :comments ], :limit => 2, :offset => 10,
+ :where => [ "authors.name = ? and comments.body = ?", 'David', 'go crazy' ]).to_a
assert_equal 0, posts.size
end
end
def test_eager_with_has_many_and_limit_and_high_offset_and_multiple_hash_conditions
assert_queries(1) do
- posts = Post.scoped(:includes => [ :author, :comments ], :limit => 2, :offset => 10,
- :where => { 'authors.name' => 'David', 'comments.body' => 'go crazy' }).all
+ posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :offset => 10,
+ :where => { 'authors.name' => 'David', 'comments.body' => 'go crazy' }).to_a
assert_equal 0, posts.size
end
end
def test_count_eager_with_has_many_and_limit_and_high_offset
- posts = Post.scoped(:includes => [ :author, :comments ], :limit => 2, :offset => 10, :where => { 'authors.name' => 'David' }).count(:all)
+ posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :offset => 10, :where => { 'authors.name' => 'David' }).count(:all)
assert_equal 0, posts
end
def test_eager_with_has_many_and_limit_with_no_results
- posts = Post.scoped(:includes => [ :author, :comments ], :limit => 2, :where => "posts.title = 'magic forest'").all
+ posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :where => "posts.title = 'magic forest'").to_a
assert_equal 0, posts.size
end
@@ -597,7 +597,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_has_and_belongs_to_many_and_limit
- posts = Post.scoped(:includes => :categories, :order => "posts.id", :limit => 3).all
+ posts = Post.all.merge!(:includes => :categories, :order => "posts.id", :limit => 3).to_a
assert_equal 3, posts.size
assert_equal 2, posts[0].categories.size
assert_equal 1, posts[1].categories.size
@@ -663,7 +663,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_habtm
- posts = Post.scoped(:includes => :categories, :order => "posts.id").all
+ posts = Post.all.merge!(:includes => :categories, :order => "posts.id").to_a
assert_equal 2, posts[0].categories.size
assert_equal 1, posts[1].categories.size
assert_equal 0, posts[2].categories.size
@@ -672,23 +672,23 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_inheritance
- SpecialPost.scoped(:includes => [ :comments ]).all
+ SpecialPost.all.merge!(:includes => [ :comments ]).to_a
end
def test_eager_has_one_with_association_inheritance
- post = Post.scoped(:includes => [ :very_special_comment ]).find(4)
+ post = Post.all.merge!(:includes => [ :very_special_comment ]).find(4)
assert_equal "VerySpecialComment", post.very_special_comment.class.to_s
end
def test_eager_has_many_with_association_inheritance
- post = Post.scoped(:includes => [ :special_comments ]).find(4)
+ post = Post.all.merge!(:includes => [ :special_comments ]).find(4)
post.special_comments.each do |special_comment|
assert special_comment.is_a?(SpecialComment)
end
end
def test_eager_habtm_with_association_inheritance
- post = Post.scoped(:includes => [ :special_categories ]).find(6)
+ post = Post.all.merge!(:includes => [ :special_categories ]).find(6)
assert_equal 1, post.special_categories.size
post.special_categories.each do |special_category|
assert_equal "SpecialCategory", special_category.class.to_s
@@ -697,7 +697,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_has_one_dependent_does_not_destroy_dependent
assert_not_nil companies(:first_firm).account
- f = Firm.scoped(:includes => :account,
+ f = Firm.all.merge!(:includes => :account,
:where => ["companies.name = ?", "37signals"]).first
assert_not_nil f.account
assert_equal companies(:first_firm, :reload).account, f.account
@@ -712,22 +712,22 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_invalid_association_reference
assert_raise(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
- Post.scoped(:includes=> :monkeys ).find(6)
+ Post.all.merge!(:includes=> :monkeys ).find(6)
}
assert_raise(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
- Post.scoped(:includes=>[ :monkeys ]).find(6)
+ Post.all.merge!(:includes=>[ :monkeys ]).find(6)
}
assert_raise(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
- Post.scoped(:includes=>[ 'monkeys' ]).find(6)
+ Post.all.merge!(:includes=>[ 'monkeys' ]).find(6)
}
assert_raise(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys, :elephants") {
- Post.scoped(:includes=>[ :monkeys, :elephants ]).find(6)
+ Post.all.merge!(:includes=>[ :monkeys, :elephants ]).find(6)
}
end
def test_eager_with_default_scope
developer = EagerDeveloperWithDefaultScope.where(:name => 'David').first
- projects = Project.order(:id).all
+ projects = Project.order(:id).to_a
assert_no_queries do
assert_equal(projects, developer.projects)
end
@@ -735,7 +735,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_default_scope_as_class_method
developer = EagerDeveloperWithClassMethodDefaultScope.where(:name => 'David').first
- projects = Project.order(:id).all
+ projects = Project.order(:id).to_a
assert_no_queries do
assert_equal(projects, developer.projects)
end
@@ -743,7 +743,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_default_scope_as_lambda
developer = EagerDeveloperWithLambdaDefaultScope.where(:name => 'David').first
- projects = Project.order(:id).all
+ projects = Project.order(:id).to_a
assert_no_queries do
assert_equal(projects, developer.projects)
end
@@ -751,7 +751,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_default_scope_as_block
developer = EagerDeveloperWithBlockDefaultScope.where(:name => 'David').first
- projects = Project.order(:id).all
+ projects = Project.order(:id).to_a
assert_no_queries do
assert_equal(projects, developer.projects)
end
@@ -759,58 +759,58 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_default_scope_as_callable
developer = EagerDeveloperWithCallableDefaultScope.where(:name => 'David').first
- projects = Project.order(:id).all
+ projects = Project.order(:id).to_a
assert_no_queries do
assert_equal(projects, developer.projects)
end
end
def find_all_ordered(className, include=nil)
- className.scoped(:order=>"#{className.table_name}.#{className.primary_key}", :includes=>include).all
+ className.all.merge!(:order=>"#{className.table_name}.#{className.primary_key}", :includes=>include).to_a
end
def test_limited_eager_with_order
assert_equal(
posts(:thinking, :sti_comments),
- Post.scoped(
+ Post.all.merge!(
:includes => [:author, :comments], :where => { 'authors.name' => 'David' },
:order => 'UPPER(posts.title)', :limit => 2, :offset => 1
- ).all
+ ).to_a
)
assert_equal(
posts(:sti_post_and_comments, :sti_comments),
- Post.scoped(
+ Post.all.merge!(
:includes => [:author, :comments], :where => { 'authors.name' => 'David' },
:order => 'UPPER(posts.title) DESC', :limit => 2, :offset => 1
- ).all
+ ).to_a
)
end
def test_limited_eager_with_multiple_order_columns
assert_equal(
posts(:thinking, :sti_comments),
- Post.scoped(
+ Post.all.merge!(
:includes => [:author, :comments], :where => { 'authors.name' => 'David' },
:order => ['UPPER(posts.title)', 'posts.id'], :limit => 2, :offset => 1
- ).all
+ ).to_a
)
assert_equal(
posts(:sti_post_and_comments, :sti_comments),
- Post.scoped(
+ Post.all.merge!(
:includes => [:author, :comments], :where => { 'authors.name' => 'David' },
:order => ['UPPER(posts.title) DESC', 'posts.id'], :limit => 2, :offset => 1
- ).all
+ ).to_a
)
end
def test_limited_eager_with_numeric_in_association
assert_equal(
people(:david, :susan),
- Person.references(:number1_fans_people).scoped(
+ Person.references(:number1_fans_people).merge(
:includes => [:readers, :primary_contact, :number1_fan],
:where => "number1_fans_people.first_name like 'M%'",
:order => 'people.id', :limit => 2, :offset => 0
- ).all
+ ).to_a
)
end
@@ -823,9 +823,9 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_polymorphic_type_condition
- post = Post.scoped(:includes => :taggings).find(posts(:thinking).id)
+ post = Post.all.merge!(:includes => :taggings).find(posts(:thinking).id)
assert post.taggings.include?(taggings(:thinking_general))
- post = SpecialPost.scoped(:includes => :taggings).find(posts(:thinking).id)
+ post = SpecialPost.all.merge!(:includes => :taggings).find(posts(:thinking).id)
assert post.taggings.include?(taggings(:thinking_general))
end
@@ -876,13 +876,13 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
end
def test_eager_with_valid_association_as_string_not_symbol
- assert_nothing_raised { Post.scoped(:includes => 'comments').all }
+ assert_nothing_raised { Post.all.merge!(:includes => 'comments').to_a }
end
def test_eager_with_floating_point_numbers
assert_queries(2) do
# Before changes, the floating point numbers will be interpreted as table names and will cause this to run in one query
- Comment.scoped(:where => "123.456 = 123.456", :includes => :post).all
+ Comment.all.merge!(:where => "123.456 = 123.456", :includes => :post).to_a
end
end
@@ -936,21 +936,21 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_load_with_sti_sharing_association
assert_queries(2) do #should not do 1 query per subclass
- Comment.includes(:post).all
+ Comment.includes(:post).to_a
end
end
def test_conditions_on_join_table_with_include_and_limit
- assert_equal 3, Developer.scoped(:includes => 'projects', :where => { 'developers_projects.access_level' => 1 }, :limit => 5).all.size
+ assert_equal 3, Developer.all.merge!(:includes => 'projects', :where => { 'developers_projects.access_level' => 1 }, :limit => 5).to_a.size
end
def test_order_on_join_table_with_include_and_limit
- assert_equal 5, Developer.scoped(:includes => 'projects', :order => 'developers_projects.joined_on DESC', :limit => 5).all.size
+ assert_equal 5, Developer.all.merge!(:includes => 'projects', :order => 'developers_projects.joined_on DESC', :limit => 5).to_a.size
end
def test_eager_loading_with_order_on_joined_table_preloads
posts = assert_queries(2) do
- Post.scoped(:joins => :comments, :includes => :author, :order => 'comments.id DESC').all
+ Post.all.merge!(:joins => :comments, :includes => :author, :order => 'comments.id DESC').to_a
end
assert_equal posts(:eager_other), posts[1]
assert_equal authors(:mary), assert_no_queries { posts[1].author}
@@ -958,37 +958,37 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_loading_with_conditions_on_joined_table_preloads
posts = assert_queries(2) do
- Post.scoped(:select => 'distinct posts.*', :includes => :author, :joins => [:comments], :where => "comments.body like 'Thank you%'", :order => 'posts.id').all
+ Post.all.merge!(:select => 'distinct posts.*', :includes => :author, :joins => [:comments], :where => "comments.body like 'Thank you%'", :order => 'posts.id').to_a
end
assert_equal [posts(:welcome)], posts
assert_equal authors(:david), assert_no_queries { posts[0].author}
posts = assert_queries(2) do
- Post.scoped(:select => 'distinct posts.*', :includes => :author, :joins => [:comments], :where => "comments.body like 'Thank you%'", :order => 'posts.id').all
+ Post.all.merge!(:select => 'distinct posts.*', :includes => :author, :joins => [:comments], :where => "comments.body like 'Thank you%'", :order => 'posts.id').to_a
end
assert_equal [posts(:welcome)], posts
assert_equal authors(:david), assert_no_queries { posts[0].author}
posts = assert_queries(2) do
- Post.scoped(:includes => :author, :joins => {:taggings => :tag}, :where => "tags.name = 'General'", :order => 'posts.id').all
+ Post.all.merge!(:includes => :author, :joins => {:taggings => :tag}, :where => "tags.name = 'General'", :order => 'posts.id').to_a
end
assert_equal posts(:welcome, :thinking), posts
posts = assert_queries(2) do
- Post.scoped(:includes => :author, :joins => {:taggings => {:tag => :taggings}}, :where => "taggings_tags.super_tag_id=2", :order => 'posts.id').all
+ Post.all.merge!(:includes => :author, :joins => {:taggings => {:tag => :taggings}}, :where => "taggings_tags.super_tag_id=2", :order => 'posts.id').to_a
end
assert_equal posts(:welcome, :thinking), posts
end
def test_eager_loading_with_conditions_on_string_joined_table_preloads
posts = assert_queries(2) do
- Post.scoped(:select => 'distinct posts.*', :includes => :author, :joins => "INNER JOIN comments on comments.post_id = posts.id", :where => "comments.body like 'Thank you%'", :order => 'posts.id').all
+ Post.all.merge!(:select => 'distinct posts.*', :includes => :author, :joins => "INNER JOIN comments on comments.post_id = posts.id", :where => "comments.body like 'Thank you%'", :order => 'posts.id').to_a
end
assert_equal [posts(:welcome)], posts
assert_equal authors(:david), assert_no_queries { posts[0].author}
posts = assert_queries(2) do
- Post.scoped(:select => 'distinct posts.*', :includes => :author, :joins => ["INNER JOIN comments on comments.post_id = posts.id"], :where => "comments.body like 'Thank you%'", :order => 'posts.id').all
+ Post.all.merge!(:select => 'distinct posts.*', :includes => :author, :joins => ["INNER JOIN comments on comments.post_id = posts.id"], :where => "comments.body like 'Thank you%'", :order => 'posts.id').to_a
end
assert_equal [posts(:welcome)], posts
assert_equal authors(:david), assert_no_queries { posts[0].author}
@@ -996,7 +996,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_loading_with_select_on_joined_table_preloads
posts = assert_queries(2) do
- Post.scoped(:select => 'posts.*, authors.name as author_name', :includes => :comments, :joins => :author, :order => 'posts.id').all
+ Post.all.merge!(:select => 'posts.*, authors.name as author_name', :includes => :comments, :joins => :author, :order => 'posts.id').to_a
end
assert_equal 'David', posts[0].author_name
assert_equal posts(:welcome).comments, assert_no_queries { posts[0].comments}
@@ -1004,14 +1004,14 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_loading_with_conditions_on_join_model_preloads
authors = assert_queries(2) do
- Author.scoped(:includes => :author_address, :joins => :comments, :where => "posts.title like 'Welcome%'").all
+ Author.all.merge!(:includes => :author_address, :joins => :comments, :where => "posts.title like 'Welcome%'").to_a
end
assert_equal authors(:david), authors[0]
assert_equal author_addresses(:david_address), authors[0].author_address
end
def test_preload_belongs_to_uses_exclusive_scope
- people = Person.males.scoped(:includes => :primary_contact).all
+ people = Person.males.merge(:includes => :primary_contact).to_a
assert_not_equal people.length, 0
people.each do |person|
assert_no_queries {assert_not_nil person.primary_contact}
@@ -1020,7 +1020,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_preload_has_many_uses_exclusive_scope
- people = Person.males.includes(:agents).all
+ people = Person.males.includes(:agents).to_a
people.each do |person|
assert_equal Person.find(person.id).agents, person.agents
end
@@ -1038,9 +1038,9 @@ class EagerAssociationTest < ActiveRecord::TestCase
expected = Firm.find(1).clients_using_primary_key.sort_by(&:name)
# Oracle adapter truncates alias to 30 characters
if current_adapter?(:OracleAdapter)
- firm = Firm.scoped(:includes => :clients_using_primary_key, :order => 'clients_using_primary_keys_companies'[0,30]+'.name').find(1)
+ firm = Firm.all.merge!(:includes => :clients_using_primary_key, :order => 'clients_using_primary_keys_companies'[0,30]+'.name').find(1)
else
- firm = Firm.scoped(:includes => :clients_using_primary_key, :order => 'clients_using_primary_keys_companies.name').find(1)
+ firm = Firm.all.merge!(:includes => :clients_using_primary_key, :order => 'clients_using_primary_keys_companies.name').find(1)
end
assert_no_queries do
assert_equal expected, firm.clients_using_primary_key
@@ -1049,7 +1049,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_preload_has_one_using_primary_key
expected = accounts(:signals37)
- firm = Firm.scoped(:includes => :account_using_primary_key, :order => 'companies.id').first
+ firm = Firm.all.merge!(:includes => :account_using_primary_key, :order => 'companies.id').first
assert_no_queries do
assert_equal expected, firm.account_using_primary_key
end
@@ -1057,7 +1057,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_include_has_one_using_primary_key
expected = accounts(:signals37)
- firm = Firm.scoped(:includes => :account_using_primary_key, :order => 'accounts.id').all.detect {|f| f.id == 1}
+ firm = Firm.all.merge!(:includes => :account_using_primary_key, :order => 'accounts.id').to_a.detect {|f| f.id == 1}
assert_no_queries do
assert_equal expected, firm.account_using_primary_key
end
@@ -1121,7 +1121,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_deep_including_through_habtm
- posts = Post.scoped(:includes => {:categories => :categorizations}, :order => "posts.id").all
+ posts = Post.all.merge!(:includes => {:categories => :categorizations}, :order => "posts.id").to_a
assert_no_queries { assert_equal 2, posts[0].categories[0].categorizations.length }
assert_no_queries { assert_equal 1, posts[0].categories[1].categorizations.length }
assert_no_queries { assert_equal 2, posts[1].categories[0].categorizations.length }
diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb
index d7c489c2b5..bd5a426ca8 100644
--- a/activerecord/test/cases/associations/extension_test.rb
+++ b/activerecord/test/cases/associations/extension_test.rb
@@ -64,14 +64,14 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
def test_proxy_association_after_scoped
post = posts(:welcome)
assert_equal post.association(:comments), post.comments.the_association
- assert_equal post.association(:comments), post.comments.scoped.the_association
+ assert_equal post.association(:comments), post.comments.where('1=1').the_association
end
private
def extension_name(model)
- builder = ActiveRecord::Associations::Builder::HasMany.new(model, :association_name, {}) { }
+ builder = ActiveRecord::Associations::Builder::HasMany.new(model, :association_name, nil, {}) { }
builder.send(:wrap_block_extension)
- builder.options[:extend].first.name
+ builder.extension_module.name
end
end
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index 24bb4adf0a..a9e18dd8fe 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -65,16 +65,6 @@ class DeveloperWithSymbolsForKeys < ActiveRecord::Base
:foreign_key => "developer_id"
end
-class DeveloperWithCounterSQL < ActiveRecord::Base
- self.table_name = 'developers'
- has_and_belongs_to_many :projects,
- :class_name => "DeveloperWithCounterSQL",
- :join_table => "developers_projects",
- :association_foreign_key => "project_id",
- :foreign_key => "developer_id",
- :counter_sql => proc { "SELECT COUNT(*) AS count_all FROM projects INNER JOIN developers_projects ON projects.id = developers_projects.project_id WHERE developers_projects.developer_id =#{id}" }
-end
-
class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
:parrots, :pirates, :parrots_pirates, :treasures, :price_estimates, :tags, :taggings
@@ -356,36 +346,11 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_deleting_array
david = Developer.find(1)
david.projects.reload
- david.projects.delete(Project.all)
+ david.projects.delete(Project.to_a)
assert_equal 0, david.projects.size
assert_equal 0, david.projects(true).size
end
- def test_deleting_with_sql
- david = Developer.find(1)
- active_record = Project.find(1)
- active_record.developers.reload
- assert_equal 3, active_record.developers_by_sql.size
-
- active_record.developers_by_sql.delete(david)
- assert_equal 2, active_record.developers_by_sql(true).size
- end
-
- def test_deleting_array_with_sql
- active_record = Project.find(1)
- active_record.developers.reload
- assert_equal 3, active_record.developers_by_sql.size
-
- active_record.developers_by_sql.delete(Developer.all)
- assert_equal 0, active_record.developers_by_sql(true).size
- end
-
- def test_deleting_all_with_sql
- project = Project.find(1)
- project.developers_by_sql.delete_all
- assert_equal 0, project.developers_by_sql.size
- end
-
def test_deleting_all
david = Developer.find(1)
david.projects.reload
@@ -423,7 +388,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_destroying_many
david = Developer.find(1)
david.projects.reload
- projects = Project.all
+ projects = Project.to_a
assert_no_difference "Project.count" do
david.projects.destroy(*projects)
@@ -534,36 +499,17 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert ! project.developers.include?(developer)
end
- def test_find_in_association_with_custom_finder_sql
- assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id), "SQL find"
-
- active_record = projects(:active_record)
- active_record.developers_with_finder_sql.reload
- assert_equal developers(:david), active_record.developers_with_finder_sql.find(developers(:david).id), "Ruby find"
- end
-
- def test_find_in_association_with_custom_finder_sql_and_multiple_interpolations
- # interpolate once:
- assert_equal [developers(:david), developers(:jamis), developers(:poor_jamis)], projects(:active_record).developers_with_finder_sql, "first interpolation"
- # interpolate again, for a different project id
- assert_equal [developers(:david)], projects(:action_controller).developers_with_finder_sql, "second interpolation"
- end
-
- def test_find_in_association_with_custom_finder_sql_and_string_id
- assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id.to_s), "SQL find"
- end
-
def test_find_with_merged_options
assert_equal 1, projects(:active_record).limited_developers.size
- assert_equal 1, projects(:active_record).limited_developers.all.size
- assert_equal 3, projects(:active_record).limited_developers.limit(nil).all.size
+ assert_equal 1, projects(:active_record).limited_developers.to_a.size
+ assert_equal 3, projects(:active_record).limited_developers.limit(nil).to_a.size
end
def test_dynamic_find_should_respect_association_order
# Developers are ordered 'name DESC, id DESC'
high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
- assert_equal high_id_jamis, projects(:active_record).developers.scoped(:where => "name = 'Jamis'").first
+ assert_equal high_id_jamis, projects(:active_record).developers.merge(:where => "name = 'Jamis'").first
assert_equal high_id_jamis, projects(:active_record).developers.find_by_name('Jamis')
end
@@ -590,7 +536,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_in_association_with_options
- developers = projects(:active_record).developers.all
+ developers = projects(:active_record).developers.to_a
assert_equal 3, developers.size
assert_equal developers(:poor_jamis), projects(:active_record).developers.where("salary < 10000").first
@@ -636,7 +582,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
project = SpecialProject.create("name" => "Special Project")
assert developer.save
developer.projects << project
- developer.update_column("name", "Bruza")
+ developer.update_columns("name" => "Bruza")
assert_equal 1, Developer.connection.select_value(<<-end_sql).to_i
SELECT count(*) FROM developers_projects
WHERE project_id = #{project.id}
@@ -668,7 +614,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_join_table_alias
assert_equal(
3,
- Developer.references(:developers_projects_join).scoped(
+ Developer.references(:developers_projects_join).merge(
:includes => {:projects => :developers},
:where => 'developers_projects_join.joined_on IS NOT NULL'
).to_a.size
@@ -684,7 +630,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal(
3,
- Developer.references(:developers_projects_join).scoped(
+ Developer.references(:developers_projects_join).merge(
:includes => {:projects => :developers}, :where => 'developers_projects_join.joined_on IS NOT NULL',
:group => group.join(",")
).to_a.size
@@ -692,8 +638,8 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_grouped
- all_posts_from_category1 = Post.scoped(:where => "category_id = 1", :joins => :categories).all
- grouped_posts_of_category1 = Post.scoped(:where => "category_id = 1", :group => "author_id", :select => 'count(posts.id) as posts_count', :joins => :categories).all
+ all_posts_from_category1 = Post.all.merge!(:where => "category_id = 1", :joins => :categories).to_a
+ grouped_posts_of_category1 = Post.all.merge!(:where => "category_id = 1", :group => "author_id", :select => 'count(posts.id) as posts_count', :joins => :categories).to_a
assert_equal 5, all_posts_from_category1.size
assert_equal 2, grouped_posts_of_category1.size
end
@@ -788,21 +734,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal 2, david.projects.count
end
- def test_count_with_counter_sql
- developer = DeveloperWithCounterSQL.create(:name => 'tekin')
- developer.project_ids = [projects(:active_record).id]
- developer.save
- developer.reload
- assert_equal 1, developer.projects.count
- end
-
- unless current_adapter?(:PostgreSQLAdapter)
- def test_count_with_finder_sql
- assert_equal 3, projects(:active_record).developers_with_finder_sql.count
- assert_equal 3, projects(:active_record).developers_with_multiline_finder_sql.count
- end
- end
-
def test_association_proxy_transaction_method_starts_transaction_in_association_class
Post.expects(:transaction)
Category.first.posts.transaction do
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 3ea6201d60..6122104335 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -20,43 +20,6 @@ require 'models/car'
require 'models/bulb'
require 'models/engine'
-class HasManyAssociationsTestForCountWithFinderSql < ActiveRecord::TestCase
- class Invoice < ActiveRecord::Base
- has_many :custom_line_items, :class_name => 'LineItem', :finder_sql => "SELECT line_items.* from line_items"
- end
- def test_should_fail
- assert_raise(ArgumentError) do
- Invoice.create.custom_line_items.count(:conditions => {:amount => 0})
- end
- end
-end
-
-class HasManyAssociationsTestForCountWithCountSql < ActiveRecord::TestCase
- class Invoice < ActiveRecord::Base
- has_many :custom_line_items, :class_name => 'LineItem', :counter_sql => "SELECT COUNT(*) line_items.* from line_items"
- end
- def test_should_fail
- assert_raise(ArgumentError) do
- Invoice.create.custom_line_items.count(:conditions => {:amount => 0})
- end
- end
-end
-
-class HasManyAssociationsTestForCountDistinctWithFinderSql < ActiveRecord::TestCase
- class Invoice < ActiveRecord::Base
- has_many :custom_line_items, :class_name => 'LineItem', :finder_sql => "SELECT DISTINCT line_items.amount from line_items"
- end
-
- def test_should_count_distinct_results
- invoice = Invoice.new
- invoice.custom_line_items << LineItem.new(:amount => 0)
- invoice.custom_line_items << LineItem.new(:amount => 0)
- invoice.save!
-
- assert_equal 1, invoice.custom_line_items.count
- end
-end
-
class HasManyAssociationsTestForReorderWithJoinDependency < ActiveRecord::TestCase
fixtures :authors, :posts, :comments
@@ -178,7 +141,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
# would be convenient), because this would cause that scope to be applied to any callbacks etc.
def test_build_and_create_should_not_happen_within_scope
car = cars(:honda)
- scoped_count = car.foo_bulbs.scoped.where_values.count
+ scoped_count = car.foo_bulbs.where_values.count
bulb = car.foo_bulbs.build
assert_not_equal scoped_count, bulb.scope_after_initialize.where_values.count
@@ -227,19 +190,19 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
# sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
def test_counting_with_counter_sql
- assert_equal 2, Firm.scoped(:order => "id").first.clients.count
+ assert_equal 2, Firm.all.merge!(:order => "id").first.clients.count
end
def test_counting
- assert_equal 2, Firm.scoped(:order => "id").first.plain_clients.count
+ assert_equal 2, Firm.all.merge!(:order => "id").first.plain_clients.count
end
def test_counting_with_single_hash
- assert_equal 1, Firm.scoped(:order => "id").first.plain_clients.where(:name => "Microsoft").count
+ assert_equal 1, Firm.all.merge!(:order => "id").first.plain_clients.where(:name => "Microsoft").count
end
def test_counting_with_column_name_and_hash
- assert_equal 2, Firm.scoped(:order => "id").first.plain_clients.count(:name)
+ assert_equal 2, Firm.all.merge!(:order => "id").first.plain_clients.count(:name)
end
def test_counting_with_association_limit
@@ -249,7 +212,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_finding
- assert_equal 2, Firm.scoped(:order => "id").first.clients.length
+ assert_equal 2, Firm.all.merge!(:order => "id").first.clients.length
end
def test_finding_array_compatibility
@@ -258,14 +221,14 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_find_with_blank_conditions
[[], {}, nil, ""].each do |blank|
- assert_equal 2, Firm.scoped(:order => "id").first.clients.where(blank).all.size
+ assert_equal 2, Firm.all.merge!(:order => "id").first.clients.where(blank).to_a.size
end
end
def test_find_many_with_merged_options
assert_equal 1, companies(:first_firm).limited_clients.size
- assert_equal 1, companies(:first_firm).limited_clients.all.size
- assert_equal 2, companies(:first_firm).limited_clients.limit(nil).all.size
+ assert_equal 1, companies(:first_firm).limited_clients.to_a.size
+ assert_equal 2, companies(:first_firm).limited_clients.limit(nil).to_a.size
end
def test_find_should_append_to_association_order
@@ -274,7 +237,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_dynamic_find_should_respect_association_order
- assert_equal companies(:second_client), companies(:first_firm).clients_sorted_desc.scoped(:where => "type = 'Client'").first
+ assert_equal companies(:second_client), companies(:first_firm).clients_sorted_desc.where("type = 'Client'").first
assert_equal companies(:second_client), companies(:first_firm).clients_sorted_desc.find_by_type('Client')
end
@@ -284,58 +247,27 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_finding_default_orders
- assert_equal "Summit", Firm.scoped(:order => "id").first.clients.first.name
+ assert_equal "Summit", Firm.all.merge!(:order => "id").first.clients.first.name
end
def test_finding_with_different_class_name_and_order
- assert_equal "Microsoft", Firm.scoped(:order => "id").first.clients_sorted_desc.first.name
+ assert_equal "Microsoft", Firm.all.merge!(:order => "id").first.clients_sorted_desc.first.name
end
def test_finding_with_foreign_key
- assert_equal "Microsoft", Firm.scoped(:order => "id").first.clients_of_firm.first.name
+ assert_equal "Microsoft", Firm.all.merge!(:order => "id").first.clients_of_firm.first.name
end
def test_finding_with_condition
- assert_equal "Microsoft", Firm.scoped(:order => "id").first.clients_like_ms.first.name
+ assert_equal "Microsoft", Firm.all.merge!(:order => "id").first.clients_like_ms.first.name
end
def test_finding_with_condition_hash
- assert_equal "Microsoft", Firm.scoped(:order => "id").first.clients_like_ms_with_hash_conditions.first.name
+ assert_equal "Microsoft", Firm.all.merge!(:order => "id").first.clients_like_ms_with_hash_conditions.first.name
end
def test_finding_using_primary_key
- assert_equal "Summit", Firm.scoped(:order => "id").first.clients_using_primary_key.first.name
- end
-
- def test_finding_using_sql
- firm = Firm.scoped(:order => "id").first
- first_client = firm.clients_using_sql.first
- assert_not_nil first_client
- assert_equal "Microsoft", first_client.name
- assert_equal 1, firm.clients_using_sql.size
- assert_equal 1, Firm.scoped(:order => "id").first.clients_using_sql.size
- end
-
- def test_finding_using_sql_take_into_account_only_uniq_ids
- firm = Firm.scoped(:order => "id").first
- client = firm.clients_using_sql.first
- assert_equal client, firm.clients_using_sql.find(client.id, client.id)
- assert_equal client, firm.clients_using_sql.find(client.id, client.id.to_s)
- end
-
- def test_counting_using_sql
- assert_equal 1, Firm.scoped(:order => "id").first.clients_using_counter_sql.size
- assert Firm.scoped(:order => "id").first.clients_using_counter_sql.any?
- assert_equal 0, Firm.scoped(:order => "id").first.clients_using_zero_counter_sql.size
- assert !Firm.scoped(:order => "id").first.clients_using_zero_counter_sql.any?
- end
-
- def test_counting_non_existant_items_using_sql
- assert_equal 0, Firm.scoped(:order => "id").first.no_clients_using_counter_sql.size
- end
-
- def test_counting_using_finder_sql
- assert_equal 2, Firm.find(4).clients_using_sql.count
+ assert_equal "Summit", Firm.all.merge!(:order => "id").first.clients_using_primary_key.first.name
end
def test_belongs_to_sanity
@@ -346,7 +278,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_ids
- firm = Firm.scoped(:order => "id").first
+ firm = Firm.all.merge!(:order => "id").first
assert_raise(ActiveRecord::RecordNotFound) { firm.clients.find }
@@ -365,26 +297,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::RecordNotFound) { firm.clients.find(2, 99) }
end
- def test_find_string_ids_when_using_finder_sql
- firm = Firm.scoped(:order => "id").first
-
- client = firm.clients_using_finder_sql.find("2")
- assert_kind_of Client, client
-
- client_ary = firm.clients_using_finder_sql.find(["2"])
- assert_kind_of Array, client_ary
- assert_equal client, client_ary.first
-
- client_ary = firm.clients_using_finder_sql.find("2", "3")
- assert_kind_of Array, client_ary
- assert_equal 2, client_ary.size
- assert client_ary.include?(client)
- end
-
def test_find_all
- firm = Firm.scoped(:order => "id").first
- assert_equal 2, firm.clients.scoped(:where => "#{QUOTED_TYPE} = 'Client'").all.length
- assert_equal 1, firm.clients.scoped(:where => "name = 'Summit'").all.length
+ firm = Firm.all.merge!(:order => "id").first
+ assert_equal 2, firm.clients.where("#{QUOTED_TYPE} = 'Client'").to_a.length
+ assert_equal 1, firm.clients.where("name = 'Summit'").to_a.length
end
def test_find_each
@@ -428,29 +344,29 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_find_all_sanitized
# sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
- firm = Firm.scoped(:order => "id").first
- summit = firm.clients.scoped(:where => "name = 'Summit'").all
- assert_equal summit, firm.clients.scoped(:where => ["name = ?", "Summit"]).all
- assert_equal summit, firm.clients.scoped(:where => ["name = :name", { :name => "Summit" }]).all
+ firm = Firm.all.merge!(:order => "id").first
+ summit = firm.clients.where("name = 'Summit'").to_a
+ assert_equal summit, firm.clients.where("name = ?", "Summit").to_a
+ assert_equal summit, firm.clients.where("name = :name", { :name => "Summit" }).to_a
end
def test_find_first
- firm = Firm.scoped(:order => "id").first
+ firm = Firm.all.merge!(:order => "id").first
client2 = Client.find(2)
- assert_equal firm.clients.first, firm.clients.scoped(:order => "id").first
- assert_equal client2, firm.clients.scoped(:where => "#{QUOTED_TYPE} = 'Client'", :order => "id").first
+ assert_equal firm.clients.first, firm.clients.order("id").first
+ assert_equal client2, firm.clients.where("#{QUOTED_TYPE} = 'Client'").order("id").first
end
def test_find_first_sanitized
- firm = Firm.scoped(:order => "id").first
+ firm = Firm.all.merge!(:order => "id").first
client2 = Client.find(2)
- assert_equal client2, firm.clients.scoped(:where => ["#{QUOTED_TYPE} = ?", 'Client'], :order => "id").first
- assert_equal client2, firm.clients.scoped(:where => ["#{QUOTED_TYPE} = :type", { :type => 'Client' }], :order => "id").first
+ assert_equal client2, firm.clients.merge!(:where => ["#{QUOTED_TYPE} = ?", 'Client'], :order => "id").first
+ assert_equal client2, firm.clients.merge!(:where => ["#{QUOTED_TYPE} = :type", { :type => 'Client' }], :order => "id").first
end
def test_find_all_with_include_and_conditions
assert_nothing_raised do
- Developer.scoped(:joins => :audit_logs, :where => {'audit_logs.message' => nil, :name => 'Smith'}).all
+ Developer.all.merge!(:joins => :audit_logs, :where => {'audit_logs.message' => nil, :name => 'Smith'}).to_a
end
end
@@ -460,8 +376,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_grouped
- all_clients_of_firm1 = Client.scoped(:where => "firm_id = 1").all
- grouped_clients_of_firm1 = Client.scoped(:where => "firm_id = 1", :group => "firm_id", :select => 'firm_id, count(id) as clients_count').all
+ all_clients_of_firm1 = Client.all.merge!(:where => "firm_id = 1").to_a
+ grouped_clients_of_firm1 = Client.all.merge!(:where => "firm_id = 1", :group => "firm_id", :select => 'firm_id, count(id) as clients_count').to_a
assert_equal 2, all_clients_of_firm1.size
assert_equal 1, grouped_clients_of_firm1.size
end
@@ -519,7 +435,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_create_with_bang_on_has_many_raises_when_record_not_saved
assert_raise(ActiveRecord::RecordInvalid) do
- firm = Firm.scoped(:order => "id").first
+ firm = Firm.all.merge!(:order => "id").first
firm.plain_clients.create!
end
end
@@ -718,7 +634,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_deleting_updates_counter_cache_with_dependent_delete_all
post = posts(:welcome)
- post.update_column(:taggings_with_delete_all_count, post.taggings_count)
+ post.update_columns(taggings_with_delete_all_count: post.taggings_count)
assert_difference "post.reload.taggings_with_delete_all_count", -1 do
post.taggings_with_delete_all.delete(post.taggings_with_delete_all.first)
@@ -727,7 +643,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_deleting_updates_counter_cache_with_dependent_destroy
post = posts(:welcome)
- post.update_column(:taggings_with_destroy_count, post.taggings_count)
+ post.update_columns(taggings_with_destroy_count: post.taggings_count)
assert_difference "post.reload.taggings_with_destroy_count", -1 do
post.taggings_with_destroy.delete(post.taggings_with_destroy.first)
@@ -897,7 +813,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm = Firm.first
# break the vanilla firm_id foreign key
assert_equal 2, firm.clients.count
- firm.clients.first.update_column(:firm_id, nil)
+ firm.clients.first.update_columns(firm_id: nil)
assert_equal 1, firm.clients(true).count
assert_equal 1, firm.clients_using_primary_key_with_delete_all.count
old_record = firm.clients_using_primary_key_with_delete_all.first
@@ -1023,7 +939,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm = companies(:first_firm)
assert_equal 2, firm.clients.size
firm.destroy
- assert Client.scoped(:where => "firm_id=#{firm.id}").all.empty?
+ assert Client.all.merge!(:where => "firm_id=#{firm.id}").to_a.empty?
end
def test_dependence_for_associations_with_hash_condition
@@ -1033,7 +949,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_destroy_dependent_when_deleted_from_association
# sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
- firm = Firm.scoped(:order => "id").first
+ firm = Firm.all.merge!(:order => "id").first
assert_equal 2, firm.clients.size
client = firm.clients.first
@@ -1061,7 +977,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.destroy rescue "do nothing"
- assert_equal 2, Client.scoped(:where => "firm_id=#{firm.id}").all.size
+ assert_equal 2, Client.all.merge!(:where => "firm_id=#{firm.id}").to_a.size
end
def test_dependence_on_account
@@ -1124,11 +1040,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_adding_array_and_collection
- assert_nothing_raised { Firm.first.clients + Firm.all.last.clients }
+ assert_nothing_raised { Firm.first.clients + Firm.to_a.last.clients }
end
def test_replace_with_less
- firm = Firm.scoped(:order => "id").first
+ firm = Firm.all.merge!(:order => "id").first
firm.clients = [companies(:first_client)]
assert firm.save, "Could not save firm"
firm.reload
@@ -1142,7 +1058,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_replace_with_new
- firm = Firm.scoped(:order => "id").first
+ firm = Firm.all.merge!(:order => "id").first
firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
firm.save
firm.reload
@@ -1208,13 +1124,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [readers(:michael_welcome).id], posts(:welcome).readers_with_person_ids
end
- def test_get_ids_for_unloaded_finder_sql_associations_loads_them
- company = companies(:first_firm)
- assert !company.clients_using_sql.loaded?
- assert_equal [companies(:second_client).id], company.clients_using_sql_ids
- assert company.clients_using_sql.loaded?
- end
-
def test_get_ids_for_ordered_association
assert_equal [companies(:second_client).id, companies(:first_client).id], companies(:first_firm).clients_ordered_by_name_ids
end
@@ -1242,7 +1151,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_dynamic_find_should_respect_association_order_for_through
- assert_equal Comment.find(10), authors(:david).comments_desc.scoped(:where => "comments.type = 'SpecialComment'").first
+ assert_equal Comment.find(10), authors(:david).comments_desc.where("comments.type = 'SpecialComment'").first
assert_equal Comment.find(10), authors(:david).comments_desc.find_by_type('SpecialComment')
end
@@ -1275,17 +1184,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert ! firm.clients.loaded?
end
- def test_include_loads_collection_if_target_uses_finder_sql
- firm = companies(:first_firm)
- client = firm.clients_using_sql.first
-
- firm.reload
- assert ! firm.clients_using_sql.loaded?
- assert firm.clients_using_sql.include?(client)
- assert firm.clients_using_sql.loaded?
- end
-
-
def test_include_returns_false_for_non_matching_record_to_verify_scoping
firm = companies(:first_firm)
client = Client.create!(:name => 'Not Associated')
@@ -1431,7 +1329,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm = Namespaced::Firm.create({ :name => 'Some Company' })
firm.clients.create({ :name => 'Some Client' })
- stats = Namespaced::Firm.scoped(
+ stats = Namespaced::Firm.all.merge!(
:select => "#{Namespaced::Firm.table_name}.id, COUNT(#{Namespaced::Client.table_name}.id) AS num_clients",
:joins => :clients,
:group => "#{Namespaced::Firm.table_name}.id"
@@ -1460,14 +1358,14 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_creating_using_primary_key
- firm = Firm.scoped(:order => "id").first
+ firm = Firm.all.merge!(:order => "id").first
client = firm.clients_using_primary_key.create!(:name => 'test')
assert_equal firm.name, client.firm_name
end
def test_defining_has_many_association_with_delete_all_dependency_lazily_evaluates_target_class
ActiveRecord::Reflection::AssociationReflection.any_instance.expects(:class_name).never
- class_eval <<-EOF
+ class_eval(<<-EOF, __FILE__, __LINE__ + 1)
class DeleteAllModel < ActiveRecord::Base
has_many :nonentities, :dependent => :delete_all
end
@@ -1476,7 +1374,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_defining_has_many_association_with_nullify_dependency_lazily_evaluates_target_class
ActiveRecord::Reflection::AssociationReflection.any_instance.expects(:class_name).never
- class_eval <<-EOF
+ class_eval(<<-EOF, __FILE__, __LINE__ + 1)
class NullifyModel < ActiveRecord::Base
has_many :nonentities, :dependent => :nullify
end
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index 1c06007d86..36e5ba9660 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -327,7 +327,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_update_counter_caches_on_delete_with_dependent_destroy
post = posts(:welcome)
tag = post.tags.create!(:name => 'doomed')
- post.update_column(:tags_with_destroy_count, post.tags.count)
+ post.update_columns(tags_with_destroy_count: post.tags.count)
assert_difference ['post.reload.taggings_count', 'post.reload.tags_with_destroy_count'], -1 do
posts(:welcome).tags_with_destroy.delete(tag)
@@ -337,7 +337,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_update_counter_caches_on_delete_with_dependent_nullify
post = posts(:welcome)
tag = post.tags.create!(:name => 'doomed')
- post.update_column(:tags_with_nullify_count, post.tags.count)
+ post.update_columns(tags_with_nullify_count: post.tags.count)
assert_no_difference 'post.reload.taggings_count' do
assert_difference 'post.reload.tags_with_nullify_count', -1 do
@@ -706,7 +706,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_through_association_readonly_should_be_false
assert !people(:michael).posts.first.readonly?
- assert !people(:michael).posts.all.first.readonly?
+ assert !people(:michael).posts.to_a.first.readonly?
end
def test_can_update_through_association
@@ -742,7 +742,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_has_many_through_with_default_scope_on_join_model
- assert_equal posts(:welcome).comments.order('id').all, authors(:david).comments_on_first_posts
+ assert_equal posts(:welcome).comments.order('id').to_a, authors(:david).comments_on_first_posts
end
def test_create_has_many_through_with_default_scope_on_join_model
@@ -813,13 +813,6 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert post[:author_count].nil?
end
- def test_interpolated_conditions
- post = posts(:welcome)
- assert !post.tags.empty?
- assert_equal post.tags, post.interpolated_tags
- assert_equal post.tags, post.interpolated_tags_2
- end
-
def test_primary_key_option_on_source
post = posts(:welcome)
category = categories(:general)
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index 88ec65706c..3ba2987f14 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -25,13 +25,13 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_queries(1) { assert_nil firm.account }
assert_queries(0) { assert_nil firm.account }
- firms = Firm.scoped(:includes => :account).all
+ firms = Firm.all.merge!(:includes => :account).to_a
assert_queries(0) { firms.each(&:account) }
end
def test_with_select
assert_equal Firm.find(1).account_with_select.attributes.size, 2
- assert_equal Firm.scoped(:includes => :account_with_select).find(1).account_with_select.attributes.size, 2
+ assert_equal Firm.all.merge!(:includes => :account_with_select).find(1).account_with_select.attributes.size, 2
end
def test_finding_using_primary_key
@@ -346,14 +346,14 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_nothing_raised do
Firm.find(@firm.id).save!
- Firm.scoped(:includes => :account).find(@firm.id).save!
+ Firm.all.merge!(:includes => :account).find(@firm.id).save!
end
@firm.account.destroy
assert_nothing_raised do
Firm.find(@firm.id).save!
- Firm.scoped(:includes => :account).find(@firm.id).save!
+ Firm.all.merge!(:includes => :account).find(@firm.id).save!
end
end
diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb
index 94b9639e57..90c557e886 100644
--- a/activerecord/test/cases/associations/has_one_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb
@@ -73,7 +73,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_eager_loading
members = assert_queries(3) do #base table, through table, clubs table
- Member.scoped(:includes => :club, :where => ["name = ?", "Groucho Marx"]).all
+ Member.all.merge!(:includes => :club, :where => ["name = ?", "Groucho Marx"]).to_a
end
assert_equal 1, members.size
assert_not_nil assert_no_queries {members[0].club}
@@ -81,7 +81,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_eager_loading_through_polymorphic
members = assert_queries(3) do #base table, through table, clubs table
- Member.scoped(:includes => :sponsor_club, :where => ["name = ?", "Groucho Marx"]).all
+ Member.all.merge!(:includes => :sponsor_club, :where => ["name = ?", "Groucho Marx"]).to_a
end
assert_equal 1, members.size
assert_not_nil assert_no_queries {members[0].sponsor_club}
@@ -89,14 +89,14 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_with_conditions_eager_loading
# conditions on the through table
- assert_equal clubs(:moustache_club), Member.scoped(:includes => :favourite_club).find(@member.id).favourite_club
- memberships(:membership_of_favourite_club).update_column(:favourite, false)
- assert_equal nil, Member.scoped(:includes => :favourite_club).find(@member.id).reload.favourite_club
+ assert_equal clubs(:moustache_club), Member.all.merge!(:includes => :favourite_club).find(@member.id).favourite_club
+ memberships(:membership_of_favourite_club).update_columns(favourite: false)
+ assert_equal nil, Member.all.merge!(:includes => :favourite_club).find(@member.id).reload.favourite_club
# conditions on the source table
- assert_equal clubs(:moustache_club), Member.scoped(:includes => :hairy_club).find(@member.id).hairy_club
- clubs(:moustache_club).update_column(:name, "Association of Clean-Shaven Persons")
- assert_equal nil, Member.scoped(:includes => :hairy_club).find(@member.id).reload.hairy_club
+ assert_equal clubs(:moustache_club), Member.all.merge!(:includes => :hairy_club).find(@member.id).hairy_club
+ clubs(:moustache_club).update_columns(name: "Association of Clean-Shaven Persons")
+ assert_equal nil, Member.all.merge!(:includes => :hairy_club).find(@member.id).reload.hairy_club
end
def test_has_one_through_polymorphic_with_source_type
@@ -104,14 +104,14 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
end
def test_eager_has_one_through_polymorphic_with_source_type
- clubs = Club.scoped(:includes => :sponsored_member, :where => ["name = ?","Moustache and Eyebrow Fancier Club"]).all
+ clubs = Club.all.merge!(:includes => :sponsored_member, :where => ["name = ?","Moustache and Eyebrow Fancier Club"]).to_a
# Only the eyebrow fanciers club has a sponsored_member
assert_not_nil assert_no_queries {clubs[0].sponsored_member}
end
def test_has_one_through_nonpreload_eagerloading
members = assert_queries(1) do
- Member.scoped(:includes => :club, :where => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name').all #force fallback
+ Member.all.merge!(:includes => :club, :where => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name').to_a #force fallback
end
assert_equal 1, members.size
assert_not_nil assert_no_queries {members[0].club}
@@ -119,7 +119,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_nonpreload_eager_loading_through_polymorphic
members = assert_queries(1) do
- Member.scoped(:includes => :sponsor_club, :where => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name').all #force fallback
+ Member.all.merge!(:includes => :sponsor_club, :where => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name').to_a #force fallback
end
assert_equal 1, members.size
assert_not_nil assert_no_queries {members[0].sponsor_club}
@@ -128,7 +128,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_nonpreload_eager_loading_through_polymorphic_with_more_than_one_through_record
Sponsor.new(:sponsor_club => clubs(:crazy_club), :sponsorable => members(:groucho)).save!
members = assert_queries(1) do
- Member.scoped(:includes => :sponsor_club, :where => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name DESC').all #force fallback
+ Member.all.merge!(:includes => :sponsor_club, :where => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name DESC').to_a #force fallback
end
assert_equal 1, members.size
assert_not_nil assert_no_queries { members[0].sponsor_club }
@@ -197,7 +197,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
@member.member_detail = @member_detail
@member.organization = @organization
@member_details = assert_queries(3) do
- MemberDetail.scoped(:includes => :member_type).all
+ MemberDetail.all.merge!(:includes => :member_type).to_a
end
@new_detail = @member_details[0]
assert @new_detail.send(:association, :member_type).loaded?
@@ -210,14 +210,14 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
assert_nothing_raised do
Club.find(@club.id).save!
- Club.scoped(:includes => :sponsored_member).find(@club.id).save!
+ Club.all.merge!(:includes => :sponsored_member).find(@club.id).save!
end
@club.sponsor.destroy
assert_nothing_raised do
Club.find(@club.id).save!
- Club.scoped(:includes => :sponsored_member).find(@club.id).save!
+ Club.all.merge!(:includes => :sponsored_member).find(@club.id).save!
end
end
diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb
index 1d61d5c474..4f246f575e 100644
--- a/activerecord/test/cases/associations/inner_join_association_test.rb
+++ b/activerecord/test/cases/associations/inner_join_association_test.rb
@@ -71,18 +71,18 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
end
def test_count_honors_implicit_inner_joins
- real_count = Author.scoped.to_a.sum{|a| a.posts.count }
+ real_count = Author.all.to_a.sum{|a| a.posts.count }
assert_equal real_count, Author.joins(:posts).count, "plain inner join count should match the number of referenced posts records"
end
def test_calculate_honors_implicit_inner_joins
- real_count = Author.scoped.to_a.sum{|a| a.posts.count }
+ real_count = Author.all.to_a.sum{|a| a.posts.count }
assert_equal real_count, Author.joins(:posts).calculate(:count, 'authors.id'), "plain inner join count should match the number of referenced posts records"
end
def test_calculate_honors_implicit_inner_joins_and_distinct_and_conditions
- real_count = Author.scoped.to_a.select {|a| a.posts.any? {|p| p.title =~ /^Welcome/} }.length
- authors_with_welcoming_post_titles = Author.scoped(:joins => :posts, :where => "posts.title like 'Welcome%'").calculate(:count, 'authors.id', :distinct => true)
+ real_count = Author.all.to_a.select {|a| a.posts.any? {|p| p.title =~ /^Welcome/} }.length
+ authors_with_welcoming_post_titles = Author.all.merge!(:joins => :posts, :where => "posts.title like 'Welcome%'").calculate(:count, 'authors.id', :distinct => true)
assert_equal real_count, authors_with_welcoming_post_titles, "inner join and conditions should have only returned authors posting titles starting with 'Welcome'"
end
diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb
index f35ffb2994..8cb8a5a861 100644
--- a/activerecord/test/cases/associations/inverse_associations_test.rb
+++ b/activerecord/test/cases/associations/inverse_associations_test.rb
@@ -96,7 +96,7 @@ class InverseHasOneTests < ActiveRecord::TestCase
def test_parent_instance_should_be_shared_with_eager_loaded_child_on_find
- m = Man.scoped(:where => {:name => 'Gordon'}, :includes => :face).first
+ m = Man.all.merge!(:where => {:name => 'Gordon'}, :includes => :face).first
f = m.face
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
m.name = 'Bongo'
@@ -104,7 +104,7 @@ class InverseHasOneTests < ActiveRecord::TestCase
f.man.name = 'Mungo'
assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance"
- m = Man.scoped(:where => {:name => 'Gordon'}, :includes => :face, :order => 'faces.id').first
+ m = Man.all.merge!(:where => {:name => 'Gordon'}, :includes => :face, :order => 'faces.id').first
f = m.face
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
m.name = 'Bongo'
@@ -179,7 +179,7 @@ class InverseHasManyTests < ActiveRecord::TestCase
end
def test_parent_instance_should_be_shared_with_eager_loaded_children
- m = Man.scoped(:where => {:name => 'Gordon'}, :includes => :interests).first
+ m = Man.all.merge!(:where => {:name => 'Gordon'}, :includes => :interests).first
is = m.interests
is.each do |i|
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
@@ -189,7 +189,7 @@ class InverseHasManyTests < ActiveRecord::TestCase
assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance"
end
- m = Man.scoped(:where => {:name => 'Gordon'}, :includes => :interests, :order => 'interests.id').first
+ m = Man.all.merge!(:where => {:name => 'Gordon'}, :includes => :interests, :order => 'interests.id').first
is = m.interests
is.each do |i|
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
@@ -278,7 +278,7 @@ class InverseBelongsToTests < ActiveRecord::TestCase
end
def test_eager_loaded_child_instance_should_be_shared_with_parent_on_find
- f = Face.scoped(:includes => :man, :where => {:description => 'trusting'}).first
+ f = Face.all.merge!(:includes => :man, :where => {:description => 'trusting'}).first
m = f.man
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
f.description = 'gormless'
@@ -286,7 +286,7 @@ class InverseBelongsToTests < ActiveRecord::TestCase
m.face.description = 'pleasing'
assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance"
- f = Face.scoped(:includes => :man, :order => 'men.id', :where => {:description => 'trusting'}).first
+ f = Face.all.merge!(:includes => :man, :order => 'men.id', :where => {:description => 'trusting'}).first
m = f.man
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
f.description = 'gormless'
@@ -351,7 +351,7 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase
fixtures :men, :faces, :interests
def test_child_instance_should_be_shared_with_parent_on_find
- f = Face.scoped(:where => {:description => 'confused'}).first
+ f = Face.all.merge!(:where => {:description => 'confused'}).first
m = f.polymorphic_man
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same before changes to child instance"
f.description = 'gormless'
@@ -361,7 +361,7 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase
end
def test_eager_loaded_child_instance_should_be_shared_with_parent_on_find
- f = Face.scoped(:where => {:description => 'confused'}, :includes => :man).first
+ f = Face.all.merge!(:where => {:description => 'confused'}, :includes => :man).first
m = f.polymorphic_man
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same before changes to child instance"
f.description = 'gormless'
@@ -369,7 +369,7 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase
m.polymorphic_face.description = 'pleasing'
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to parent-owned instance"
- f = Face.scoped(:where => {:description => 'confused'}, :includes => :man, :order => 'men.id').first
+ f = Face.all.merge!(:where => {:description => 'confused'}, :includes => :man, :order => 'men.id').first
m = f.polymorphic_man
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same before changes to child instance"
f.description = 'gormless'
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index 783b83631c..1c8090de8a 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -51,7 +51,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_uniq_through_find
- assert_equal 1, authors(:mary).unique_categorized_posts.all.size
+ assert_equal 1, authors(:mary).unique_categorized_posts.to_a.size
end
def test_polymorphic_has_many_going_through_join_model
@@ -175,7 +175,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_delete_polymorphic_has_many_with_delete_all
assert_equal 1, posts(:welcome).taggings.count
- posts(:welcome).taggings.first.update_column :taggable_type, 'PostWithHasManyDeleteAll'
+ posts(:welcome).taggings.first.update_columns taggable_type: 'PostWithHasManyDeleteAll'
post = find_post_with_dependency(1, :has_many, :taggings, :delete_all)
old_count = Tagging.count
@@ -186,7 +186,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_delete_polymorphic_has_many_with_destroy
assert_equal 1, posts(:welcome).taggings.count
- posts(:welcome).taggings.first.update_column :taggable_type, 'PostWithHasManyDestroy'
+ posts(:welcome).taggings.first.update_columns taggable_type: 'PostWithHasManyDestroy'
post = find_post_with_dependency(1, :has_many, :taggings, :destroy)
old_count = Tagging.count
@@ -197,7 +197,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_delete_polymorphic_has_many_with_nullify
assert_equal 1, posts(:welcome).taggings.count
- posts(:welcome).taggings.first.update_column :taggable_type, 'PostWithHasManyNullify'
+ posts(:welcome).taggings.first.update_columns taggable_type: 'PostWithHasManyNullify'
post = find_post_with_dependency(1, :has_many, :taggings, :nullify)
old_count = Tagging.count
@@ -208,7 +208,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_delete_polymorphic_has_one_with_destroy
assert posts(:welcome).tagging
- posts(:welcome).tagging.update_column :taggable_type, 'PostWithHasOneDestroy'
+ posts(:welcome).tagging.update_columns taggable_type: 'PostWithHasOneDestroy'
post = find_post_with_dependency(1, :has_one, :tagging, :destroy)
old_count = Tagging.count
@@ -219,7 +219,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_delete_polymorphic_has_one_with_nullify
assert posts(:welcome).tagging
- posts(:welcome).tagging.update_column :taggable_type, 'PostWithHasOneNullify'
+ posts(:welcome).tagging.update_columns taggable_type: 'PostWithHasOneNullify'
post = find_post_with_dependency(1, :has_one, :tagging, :nullify)
old_count = Tagging.count
@@ -233,8 +233,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_include_has_many_through
- posts = Post.scoped(:order => 'posts.id').all
- posts_with_authors = Post.scoped(:includes => :authors, :order => 'posts.id').all
+ posts = Post.all.merge!(:order => 'posts.id').to_a
+ posts_with_authors = Post.all.merge!(:includes => :authors, :order => 'posts.id').to_a
assert_equal posts.length, posts_with_authors.length
posts.length.times do |i|
assert_equal posts[i].authors.length, assert_no_queries { posts_with_authors[i].authors.length }
@@ -258,8 +258,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_include_polymorphic_has_many_through
- posts = Post.scoped(:order => 'posts.id').all
- posts_with_tags = Post.scoped(:includes => :tags, :order => 'posts.id').all
+ posts = Post.all.merge!(:order => 'posts.id').to_a
+ posts_with_tags = Post.all.merge!(:includes => :tags, :order => 'posts.id').to_a
assert_equal posts.length, posts_with_tags.length
posts.length.times do |i|
assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length }
@@ -267,8 +267,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_include_polymorphic_has_many
- posts = Post.scoped(:order => 'posts.id').all
- posts_with_taggings = Post.scoped(:includes => :taggings, :order => 'posts.id').all
+ posts = Post.all.merge!(:order => 'posts.id').to_a
+ posts_with_taggings = Post.all.merge!(:includes => :taggings, :order => 'posts.id').to_a
assert_equal posts.length, posts_with_taggings.length
posts.length.times do |i|
assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length }
@@ -276,7 +276,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_find_all
- assert_equal [categories(:general)], authors(:david).categories.all
+ assert_equal [categories(:general)], authors(:david).categories.to_a
end
def test_has_many_find_first
@@ -288,8 +288,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_find_conditions
- assert_equal categories(:general), authors(:david).categories.scoped(:where => "categories.name = 'General'").first
- assert_nil authors(:david).categories.scoped(:where => "categories.name = 'Technology'").first
+ assert_equal categories(:general), authors(:david).categories.where("categories.name = 'General'").first
+ assert_nil authors(:david).categories.where("categories.name = 'Technology'").first
end
def test_has_many_array_methods_called_by_method_missing
@@ -355,7 +355,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_eager_has_many_polymorphic_with_source_type
- tag_with_include = Tag.scoped(:includes => :tagged_posts).find(tags(:general).id)
+ tag_with_include = Tag.all.merge!(:includes => :tagged_posts).find(tags(:general).id)
desired = posts(:welcome, :thinking)
assert_no_queries do
# added sort by ID as otherwise test using JRuby was failing as array elements were in different order
@@ -365,20 +365,20 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_through_has_many_find_all
- assert_equal comments(:greetings), authors(:david).comments.scoped(:order => 'comments.id').all.first
+ assert_equal comments(:greetings), authors(:david).comments.order('comments.id').to_a.first
end
def test_has_many_through_has_many_find_all_with_custom_class
- assert_equal comments(:greetings), authors(:david).funky_comments.scoped(:order => 'comments.id').all.first
+ assert_equal comments(:greetings), authors(:david).funky_comments.order('comments.id').to_a.first
end
def test_has_many_through_has_many_find_first
- assert_equal comments(:greetings), authors(:david).comments.scoped(:order => 'comments.id').first
+ assert_equal comments(:greetings), authors(:david).comments.order('comments.id').first
end
def test_has_many_through_has_many_find_conditions
options = { :where => "comments.#{QUOTED_TYPE}='SpecialComment'", :order => 'comments.id' }
- assert_equal comments(:does_it_hurt), authors(:david).comments.scoped(options).first
+ assert_equal comments(:does_it_hurt), authors(:david).comments.merge(options).first
end
def test_has_many_through_has_many_find_by_id
@@ -402,7 +402,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_eager_load_has_many_through_has_many
- author = Author.scoped(:where => ['name = ?', 'David'], :includes => :comments, :order => 'comments.id').first
+ author = Author.all.merge!(:where => ['name = ?', 'David'], :includes => :comments, :order => 'comments.id').first
SpecialComment.new; VerySpecialComment.new
assert_no_queries do
assert_equal [1,2,3,5,6,7,8,9,10,12], author.comments.collect(&:id)
@@ -410,7 +410,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_eager_load_has_many_through_has_many_with_conditions
- post = Post.scoped(:includes => :invalid_tags).first
+ post = Post.all.merge!(:includes => :invalid_tags).first
assert_no_queries do
post.invalid_tags
end
@@ -418,8 +418,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_eager_belongs_to_and_has_one_not_singularized
assert_nothing_raised do
- Author.scoped(:includes => :author_address).first
- AuthorAddress.scoped(:includes => :author).first
+ Author.all.merge!(:includes => :author_address).first
+ AuthorAddress.all.merge!(:includes => :author).first
end
end
@@ -625,7 +625,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_polymorphic_has_many
expected = taggings(:welcome_general)
- p = Post.scoped(:includes => :taggings).find(posts(:welcome).id)
+ p = Post.all.merge!(:includes => :taggings).find(posts(:welcome).id)
assert_no_queries {assert p.taggings.include?(expected)}
assert posts(:welcome).taggings.include?(taggings(:welcome_general))
end
@@ -633,18 +633,18 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_polymorphic_has_one
expected = posts(:welcome)
- tagging = Tagging.scoped(:includes => :taggable).find(taggings(:welcome_general).id)
+ tagging = Tagging.all.merge!(:includes => :taggable).find(taggings(:welcome_general).id)
assert_no_queries { assert_equal expected, tagging.taggable}
end
def test_polymorphic_belongs_to
- p = Post.scoped(:includes => {:taggings => :taggable}).find(posts(:welcome).id)
+ p = Post.all.merge!(:includes => {:taggings => :taggable}).find(posts(:welcome).id)
assert_no_queries {assert_equal posts(:welcome), p.taggings.first.taggable}
end
def test_preload_polymorphic_has_many_through
- posts = Post.scoped(:order => 'posts.id').all
- posts_with_tags = Post.scoped(:includes => :tags, :order => 'posts.id').all
+ posts = Post.all.merge!(:order => 'posts.id').to_a
+ posts_with_tags = Post.all.merge!(:includes => :tags, :order => 'posts.id').to_a
assert_equal posts.length, posts_with_tags.length
posts.length.times do |i|
assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length }
@@ -652,7 +652,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_preload_polymorph_many_types
- taggings = Tagging.scoped(:includes => :taggable, :where => ['taggable_type != ?', 'FakeModel']).all
+ taggings = Tagging.all.merge!(:includes => :taggable, :where => ['taggable_type != ?', 'FakeModel']).to_a
assert_no_queries do
taggings.first.taggable.id
taggings[1].taggable.id
@@ -665,13 +665,13 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_preload_nil_polymorphic_belongs_to
assert_nothing_raised do
- Tagging.scoped(:includes => :taggable, :where => ['taggable_type IS NULL']).all
+ Tagging.all.merge!(:includes => :taggable, :where => ['taggable_type IS NULL']).to_a
end
end
def test_preload_polymorphic_has_many
- posts = Post.scoped(:order => 'posts.id').all
- posts_with_taggings = Post.scoped(:includes => :taggings, :order => 'posts.id').all
+ posts = Post.all.merge!(:order => 'posts.id').to_a
+ posts_with_taggings = Post.all.merge!(:includes => :taggings, :order => 'posts.id').to_a
assert_equal posts.length, posts_with_taggings.length
posts.length.times do |i|
assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length }
@@ -679,7 +679,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_belongs_to_shared_parent
- comments = Comment.scoped(:includes => :post, :where => 'post_id = 1').all
+ comments = Comment.all.merge!(:includes => :post, :where => 'post_id = 1').to_a
assert_no_queries do
assert_equal comments.first.post, comments[1].post
end
@@ -734,7 +734,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
# create dynamic Post models to allow different dependency options
def find_post_with_dependency(post_id, association, association_name, dependency)
class_name = "PostWith#{association.to_s.classify}#{dependency.to_s.classify}"
- Post.find(post_id).update_column :type, class_name
+ Post.find(post_id).update_columns type: class_name
klass = Object.const_set(class_name, Class.new(ActiveRecord::Base))
klass.table_name = 'posts'
klass.send(association, association_name, :as => :taggable, :dependent => dependency)
diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb
index 1d0550afaf..b7c2bcad00 100644
--- a/activerecord/test/cases/associations_test.rb
+++ b/activerecord/test/cases/associations_test.rb
@@ -67,15 +67,15 @@ class AssociationsTest < ActiveRecord::TestCase
ship = Ship.create!(:name => "The good ship Dollypop")
part = ship.parts.create!(:name => "Mast")
part.mark_for_destruction
- ShipPart.find(part.id).update_column(:name, 'Deck')
+ ShipPart.find(part.id).update_columns(name: 'Deck')
ship.parts.send(:load_target)
assert_equal 'Deck', ship.parts[0].name
end
def test_include_with_order_works
- assert_nothing_raised {Account.scoped(:order => 'id', :includes => :firm).first}
- assert_nothing_raised {Account.scoped(:order => :id, :includes => :firm).first}
+ assert_nothing_raised {Account.all.merge!(:order => 'id', :includes => :firm).first}
+ assert_nothing_raised {Account.all.merge!(:order => :id, :includes => :firm).first}
end
def test_bad_collection_keys
@@ -86,7 +86,7 @@ class AssociationsTest < ActiveRecord::TestCase
def test_should_construct_new_finder_sql_after_create
person = Person.new :first_name => 'clark'
- assert_equal [], person.readers.all
+ assert_equal [], person.readers.to_a
person.save!
reader = Reader.create! :person => person, :post => Post.new(:title => "foo", :body => "bar")
assert person.readers.find(reader.id)
@@ -110,7 +110,7 @@ class AssociationsTest < ActiveRecord::TestCase
end
def test_using_limitable_reflections_helper
- using_limitable_reflections = lambda { |reflections| Tagging.scoped.send :using_limitable_reflections?, reflections }
+ using_limitable_reflections = lambda { |reflections| Tagging.all.send :using_limitable_reflections?, reflections }
belongs_to_reflections = [Tagging.reflect_on_association(:tag), Tagging.reflect_on_association(:super_tag)]
has_many_reflections = [Tag.reflect_on_association(:taggings), Developer.reflect_on_association(:projects)]
mixed_reflections = (belongs_to_reflections + has_many_reflections).uniq
@@ -131,7 +131,7 @@ class AssociationsTest < ActiveRecord::TestCase
def test_association_with_references
firm = companies(:first_firm)
- assert_equal ['foo'], firm.association_with_references.scoped.references_values
+ assert_equal ['foo'], firm.association_with_references.references_values
end
end
@@ -176,7 +176,7 @@ class AssociationProxyTest < ActiveRecord::TestCase
david = developers(:david)
assert !david.projects.loaded?
- david.update_column(:created_at, Time.now)
+ david.update_columns(created_at: Time.now)
assert !david.projects.loaded?
end
@@ -216,7 +216,7 @@ class AssociationProxyTest < ActiveRecord::TestCase
end
def test_scoped_allows_conditions
- assert developers(:david).projects.scoped(where: 'foo').where_values.include?('foo')
+ assert developers(:david).projects.merge!(where: 'foo').where_values.include?('foo')
end
end
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index fe385feb4a..c2619e51a9 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -484,9 +484,9 @@ class AttributeMethodsTest < ActiveRecord::TestCase
Topic.create(:title => 'Budget')
# Oracle does not support boolean expressions in SELECT
if current_adapter?(:OracleAdapter)
- topic = Topic.scoped(:select => "topics.*, 0 as is_test").first
+ topic = Topic.all.merge!(:select => "topics.*, 0 as is_test").first
else
- topic = Topic.scoped(:select => "topics.*, 1=2 as is_test").first
+ topic = Topic.all.merge!(:select => "topics.*, 1=2 as is_test").first
end
assert !topic.is_test?
end
@@ -495,9 +495,9 @@ class AttributeMethodsTest < ActiveRecord::TestCase
Topic.create(:title => 'Budget')
# Oracle does not support boolean expressions in SELECT
if current_adapter?(:OracleAdapter)
- topic = Topic.scoped(:select => "topics.*, 1 as is_test").first
+ topic = Topic.all.merge!(:select => "topics.*, 1 as is_test").first
else
- topic = Topic.scoped(:select => "topics.*, 2=2 as is_test").first
+ topic = Topic.all.merge!(:select => "topics.*, 2=2 as is_test").first
end
assert topic.is_test?
end
@@ -816,7 +816,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
def privatize(method_signature)
- @target.class_eval <<-private_method
+ @target.class_eval(<<-private_method, __FILE__, __LINE__ + 1)
private
def #{method_signature}
"I'm private"
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 8ef3bfef15..fd4f09ab36 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -20,22 +20,6 @@ require 'models/company'
require 'models/eye'
class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
- def test_autosave_should_be_a_valid_option_for_has_one
- assert ActiveRecord::Associations::Builder::HasOne.valid_options.include?(:autosave)
- end
-
- def test_autosave_should_be_a_valid_option_for_belongs_to
- assert ActiveRecord::Associations::Builder::BelongsTo.valid_options.include?(:autosave)
- end
-
- def test_autosave_should_be_a_valid_option_for_has_many
- assert ActiveRecord::Associations::Builder::HasMany.valid_options.include?(:autosave)
- end
-
- def test_autosave_should_be_a_valid_option_for_has_and_belongs_to_many
- assert ActiveRecord::Associations::Builder::HasAndBelongsToMany.valid_options.include?(:autosave)
- end
-
def test_should_not_add_the_same_callbacks_multiple_times_for_has_one
assert_no_difference_when_adding_callbacks_twice_for Pirate, :ship
end
@@ -155,7 +139,7 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
end
def test_not_resaved_when_unchanged
- firm = Firm.scoped(:includes => :account).first
+ firm = Firm.all.merge!(:includes => :account).first
firm.name += '-changed'
assert_queries(1) { firm.save! }
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index e34f505a02..fe69f4161a 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -131,36 +131,36 @@ class BasicsTest < ActiveRecord::TestCase
unless current_adapter?(:PostgreSQLAdapter,:OracleAdapter,:SQLServerAdapter)
def test_limit_with_comma
- assert Topic.limit("1,2").all
+ assert Topic.limit("1,2").to_a
end
end
def test_limit_without_comma
- assert_equal 1, Topic.limit("1").all.length
- assert_equal 1, Topic.limit(1).all.length
+ assert_equal 1, Topic.limit("1").to_a.length
+ assert_equal 1, Topic.limit(1).to_a.length
end
def test_invalid_limit
assert_raises(ArgumentError) do
- Topic.limit("asdfadf").all
+ Topic.limit("asdfadf").to_a
end
end
def test_limit_should_sanitize_sql_injection_for_limit_without_comas
assert_raises(ArgumentError) do
- Topic.limit("1 select * from schema").all
+ Topic.limit("1 select * from schema").to_a
end
end
def test_limit_should_sanitize_sql_injection_for_limit_with_comas
assert_raises(ArgumentError) do
- Topic.limit("1, 7 procedure help()").all
+ Topic.limit("1, 7 procedure help()").to_a
end
end
unless current_adapter?(:MysqlAdapter) || current_adapter?(:Mysql2Adapter)
def test_limit_should_allow_sql_literal
- assert_equal 1, Topic.limit(Arel.sql('2-1')).all.length
+ assert_equal 1, Topic.limit(Arel.sql('2-1')).to_a.length
end
end
@@ -349,13 +349,13 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_load
- topics = Topic.scoped(:order => 'id').all
+ topics = Topic.all.merge!(:order => 'id').to_a
assert_equal(4, topics.size)
assert_equal(topics(:first).title, topics.first.title)
end
def test_load_with_condition
- topics = Topic.scoped(:where => "author_name = 'Mary'").all
+ topics = Topic.all.merge!(:where => "author_name = 'Mary'").to_a
assert_equal(1, topics.size)
assert_equal(topics(:second).title, topics.first.title)
@@ -609,7 +609,7 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal 'value', weird.send('a$b')
assert_equal 'value', weird.read_attribute('a$b')
- weird.update_column('a$b', 'value2')
+ weird.update_columns('a$b' => 'value2')
weird.reload
assert_equal 'value2', weird.send('a$b')
assert_equal 'value2', weird.read_attribute('a$b')
@@ -1218,10 +1218,10 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_quoting_arrays
- replies = Reply.scoped(:where => [ "id IN (?)", topics(:first).replies.collect(&:id) ]).all
+ replies = Reply.all.merge!(:where => [ "id IN (?)", topics(:first).replies.collect(&:id) ]).to_a
assert_equal topics(:first).replies.size, replies.size
- replies = Reply.scoped(:where => [ "id IN (?)", [] ]).all
+ replies = Reply.all.merge!(:where => [ "id IN (?)", [] ]).to_a
assert_equal 0, replies.size
end
@@ -1556,57 +1556,57 @@ class BasicsTest < ActiveRecord::TestCase
def test_no_limit_offset
assert_nothing_raised do
- Developer.scoped(:offset => 2).all
+ Developer.all.merge!(:offset => 2).to_a
end
end
def test_find_last
last = Developer.last
- assert_equal last, Developer.scoped(:order => 'id desc').first
+ assert_equal last, Developer.all.merge!(:order => 'id desc').first
end
def test_last
- assert_equal Developer.scoped(:order => 'id desc').first, Developer.last
+ assert_equal Developer.all.merge!(:order => 'id desc').first, Developer.last
end
def test_all
- developers = Developer.all
+ developers = Developer.to_a
assert_kind_of Array, developers
- assert_equal Developer.all, developers
+ assert_equal Developer.to_a, developers
end
def test_all_with_conditions
- assert_equal Developer.scoped(:order => 'id desc').all, Developer.order('id desc').all
+ assert_equal Developer.all.merge!(:order => 'id desc').to_a, Developer.order('id desc').to_a
end
def test_find_ordered_last
- last = Developer.scoped(:order => 'developers.salary ASC').last
- assert_equal last, Developer.scoped(:order => 'developers.salary ASC').all.last
+ last = Developer.all.merge!(:order => 'developers.salary ASC').last
+ assert_equal last, Developer.all.merge!(:order => 'developers.salary ASC').to_a.last
end
def test_find_reverse_ordered_last
- last = Developer.scoped(:order => 'developers.salary DESC').last
- assert_equal last, Developer.scoped(:order => 'developers.salary DESC').all.last
+ last = Developer.all.merge!(:order => 'developers.salary DESC').last
+ assert_equal last, Developer.all.merge!(:order => 'developers.salary DESC').to_a.last
end
def test_find_multiple_ordered_last
- last = Developer.scoped(:order => 'developers.name, developers.salary DESC').last
- assert_equal last, Developer.scoped(:order => 'developers.name, developers.salary DESC').all.last
+ last = Developer.all.merge!(:order => 'developers.name, developers.salary DESC').last
+ assert_equal last, Developer.all.merge!(:order => 'developers.name, developers.salary DESC').to_a.last
end
def test_find_keeps_multiple_order_values
- combined = Developer.scoped(:order => 'developers.name, developers.salary').all
- assert_equal combined, Developer.scoped(:order => ['developers.name', 'developers.salary']).all
+ combined = Developer.all.merge!(:order => 'developers.name, developers.salary').to_a
+ assert_equal combined, Developer.all.merge!(:order => ['developers.name', 'developers.salary']).to_a
end
def test_find_keeps_multiple_group_values
- combined = Developer.scoped(:group => 'developers.name, developers.salary, developers.id, developers.created_at, developers.updated_at').all
- assert_equal combined, Developer.scoped(:group => ['developers.name', 'developers.salary', 'developers.id', 'developers.created_at', 'developers.updated_at']).all
+ combined = Developer.all.merge!(:group => 'developers.name, developers.salary, developers.id, developers.created_at, developers.updated_at').to_a
+ assert_equal combined, Developer.all.merge!(:group => ['developers.name', 'developers.salary', 'developers.id', 'developers.created_at', 'developers.updated_at']).to_a
end
def test_find_symbol_ordered_last
- last = Developer.scoped(:order => :salary).last
- assert_equal last, Developer.scoped(:order => :salary).all.last
+ last = Developer.all.merge!(:order => :salary).last
+ assert_equal last, Developer.all.merge!(:order => :salary).to_a.last
end
def test_abstract_class
@@ -1619,18 +1619,6 @@ class BasicsTest < ActiveRecord::TestCase
assert_nil AbstractCompany.table_name
end
- def test_base_class
- assert_equal LoosePerson, LoosePerson.base_class
- assert_equal LooseDescendant, LooseDescendant.base_class
- assert_equal TightPerson, TightPerson.base_class
- assert_equal TightPerson, TightDescendant.base_class
-
- assert_equal Post, Post.base_class
- assert_equal Post, SpecialPost.base_class
- assert_equal Post, StiPost.base_class
- assert_equal SubStiPost, SubStiPost.base_class
- end
-
def test_descends_from_active_record
assert !ActiveRecord::Base.descends_from_active_record?
@@ -1684,6 +1672,12 @@ class BasicsTest < ActiveRecord::TestCase
assert_kind_of String, Client.first.to_param
end
+ def test_to_param_returns_id_even_if_not_persisted
+ client = Client.new
+ client.id = 1
+ assert_equal "1", client.to_param
+ end
+
def test_inspect_class
assert_equal 'ActiveRecord::Base', ActiveRecord::Base.inspect
assert_equal 'LoosePerson(abstract)', LoosePerson.inspect
@@ -1700,8 +1694,8 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_inspect_limited_select_instance
- assert_equal %(#<Topic id: 1>), Topic.scoped(:select => 'id', :where => 'id = 1').first.inspect
- assert_equal %(#<Topic id: 1, title: "The First Topic">), Topic.scoped(:select => 'id, title', :where => 'id = 1').first.inspect
+ assert_equal %(#<Topic id: 1>), Topic.all.merge!(:select => 'id', :where => 'id = 1').first.inspect
+ assert_equal %(#<Topic id: 1, title: "The First Topic">), Topic.all.merge!(:select => 'id, title', :where => 'id = 1').first.inspect
end
def test_inspect_class_without_table
@@ -1814,7 +1808,7 @@ class BasicsTest < ActiveRecord::TestCase
def test_current_scope_is_reset
Object.const_set :UnloadablePost, Class.new(ActiveRecord::Base)
- UnloadablePost.send(:current_scope=, UnloadablePost.scoped)
+ UnloadablePost.send(:current_scope=, UnloadablePost.all)
UnloadablePost.unloadable
assert_not_nil Thread.current[:UnloadablePost_current_scope]
@@ -1890,13 +1884,13 @@ class BasicsTest < ActiveRecord::TestCase
def test_cache_key_format_for_existing_record_with_nil_updated_at
dev = Developer.first
- dev.update_column(:updated_at, nil)
+ dev.update_columns(updated_at: nil)
assert_match(/\/#{dev.id}$/, dev.cache_key)
end
def test_uniq_delegates_to_scoped
scope = stub
- Bird.stubs(:scoped).returns(mock(:uniq => scope))
+ Bird.stubs(:all).returns(mock(:uniq => scope))
assert_equal scope, Bird.uniq
end
@@ -1963,7 +1957,7 @@ class BasicsTest < ActiveRecord::TestCase
scope.expects(meth).with(:foo, :bar).returns(record)
klass = Class.new(ActiveRecord::Base)
- klass.stubs(:scoped => scope)
+ klass.stubs(:all => scope)
assert_equal record, klass.public_send(meth, :foo, :bar)
end
@@ -1971,6 +1965,14 @@ class BasicsTest < ActiveRecord::TestCase
test "scoped can take a values hash" do
klass = Class.new(ActiveRecord::Base)
- assert_equal ['foo'], klass.scoped(select: 'foo').select_values
+ assert_equal ['foo'], klass.all.merge!(select: 'foo').select_values
+ end
+
+ test "Model.to_a returns an array" do
+ assert_equal Post.all.to_a, Post.to_a
+ end
+
+ test "Model.all returns a relation" do
+ assert Post.all.is_a?(ActiveRecord::Relation)
end
end
diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb
index cdd4b49042..7583539d04 100644
--- a/activerecord/test/cases/batches_test.rb
+++ b/activerecord/test/cases/batches_test.rb
@@ -116,7 +116,7 @@ class EachTest < ActiveRecord::TestCase
end
def test_find_in_batches_should_not_ignore_the_default_scope_if_it_is_other_then_order
- special_posts_ids = SpecialPostWithDefaultScope.all.map(&:id).sort
+ special_posts_ids = SpecialPostWithDefaultScope.to_a.map(&:id).sort
posts = []
SpecialPostWithDefaultScope.find_in_batches do |batch|
posts.concat(batch)
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index e1c1e449ef..55f6ec06c0 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -40,8 +40,8 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_type_cast_calculated_value_should_convert_db_averages_of_fixnum_class_to_decimal
- assert_equal 0, NumericData.scoped.send(:type_cast_calculated_value, 0, nil, 'avg')
- assert_equal 53.0, NumericData.scoped.send(:type_cast_calculated_value, 53, nil, 'avg')
+ assert_equal 0, NumericData.all.send(:type_cast_calculated_value, 0, nil, 'avg')
+ assert_equal 53.0, NumericData.all.send(:type_cast_calculated_value, 53, nil, 'avg')
end
def test_should_get_maximum_of_field
@@ -91,24 +91,24 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_order_by_grouped_field
- c = Account.scoped(:group => :firm_id, :order => "firm_id").sum(:credit_limit)
+ c = Account.all.merge!(:group => :firm_id, :order => "firm_id").sum(:credit_limit)
assert_equal [1, 2, 6, 9], c.keys.compact
end
def test_should_order_by_calculation
- c = Account.scoped(:group => :firm_id, :order => "sum_credit_limit desc, firm_id").sum(:credit_limit)
+ c = Account.all.merge!(:group => :firm_id, :order => "sum_credit_limit desc, firm_id").sum(:credit_limit)
assert_equal [105, 60, 53, 50, 50], c.keys.collect { |k| c[k] }
assert_equal [6, 2, 9, 1], c.keys.compact
end
def test_should_limit_calculation
- c = Account.scoped(:where => "firm_id IS NOT NULL",
+ c = Account.all.merge!(:where => "firm_id IS NOT NULL",
:group => :firm_id, :order => "firm_id", :limit => 2).sum(:credit_limit)
assert_equal [1, 2], c.keys.compact
end
def test_should_limit_calculation_with_offset
- c = Account.scoped(:where => "firm_id IS NOT NULL", :group => :firm_id,
+ c = Account.all.merge!(:where => "firm_id IS NOT NULL", :group => :firm_id,
:order => "firm_id", :limit => 2, :offset => 1).sum(:credit_limit)
assert_equal [2, 6], c.keys.compact
end
@@ -159,7 +159,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_group_by_summed_field_having_condition
- c = Account.scoped(:group => :firm_id,
+ c = Account.all.merge!(:group => :firm_id,
:having => 'sum(credit_limit) > 50').sum(:credit_limit)
assert_nil c[1]
assert_equal 105, c[6]
@@ -195,7 +195,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_group_by_summed_field_with_conditions
- c = Account.scoped(:where => 'firm_id > 1',
+ c = Account.all.merge!(:where => 'firm_id > 1',
:group => :firm_id).sum(:credit_limit)
assert_nil c[1]
assert_equal 105, c[6]
@@ -203,7 +203,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_group_by_summed_field_with_conditions_and_having
- c = Account.scoped(:where => 'firm_id > 1',
+ c = Account.all.merge!(:where => 'firm_id > 1',
:group => :firm_id,
:having => 'sum(credit_limit) > 60').sum(:credit_limit)
assert_nil c[1]
@@ -326,19 +326,19 @@ class CalculationsTest < ActiveRecord::TestCase
def test_should_count_scoped_select
Account.update_all("credit_limit = NULL")
- assert_equal 0, Account.scoped(:select => "credit_limit").count
+ assert_equal 0, Account.all.merge!(:select => "credit_limit").count
end
def test_should_count_scoped_select_with_options
Account.update_all("credit_limit = NULL")
- Account.last.update_column('credit_limit', 49)
- Account.first.update_column('credit_limit', 51)
+ Account.last.update_columns('credit_limit' => 49)
+ Account.first.update_columns('credit_limit' => 51)
- assert_equal 1, Account.scoped(:select => "credit_limit").where('credit_limit >= 50').count
+ assert_equal 1, Account.all.merge!(:select => "credit_limit").where('credit_limit >= 50').count
end
def test_should_count_manual_select_with_include
- assert_equal 6, Account.scoped(:select => "DISTINCT accounts.id", :includes => :firm).count
+ assert_equal 6, Account.all.merge!(:select => "DISTINCT accounts.id", :includes => :firm).count
end
def test_count_with_column_parameter
@@ -355,7 +355,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_count_field_in_joined_table_with_group_by
- c = Account.scoped(:group => 'accounts.firm_id', :joins => :firm).count('companies.id')
+ c = Account.all.merge!(:group => 'accounts.firm_id', :joins => :firm).count('companies.id')
[1,6,2,9].each { |firm_id| assert c.keys.include?(firm_id) }
end
@@ -433,7 +433,7 @@ class CalculationsTest < ActiveRecord::TestCase
Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])
# TODO: Investigate why PG isn't being typecast
- if current_adapter?(:PostgreSQLAdapter)
+ if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:MysqlAdapter)
assert_equal "7", Company.includes(:contracts).maximum(:developer_id)
else
assert_equal 7, Company.includes(:contracts).maximum(:developer_id)
@@ -444,7 +444,7 @@ class CalculationsTest < ActiveRecord::TestCase
Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])
# TODO: Investigate why PG isn't being typecast
- if current_adapter?(:PostgreSQLAdapter)
+ if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:MysqlAdapter)
assert_equal "7", Company.includes(:contracts).minimum(:developer_id)
else
assert_equal 7, Company.includes(:contracts).minimum(:developer_id)
@@ -542,7 +542,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_plucks_with_ids
- assert_equal Company.all.map(&:id).sort, Company.ids.sort
+ assert_equal Company.to_a.map(&:id).sort, Company.ids.sort
end
def test_pluck_not_auto_table_name_prefix_if_column_included
diff --git a/activerecord/test/cases/column_test.rb b/activerecord/test/cases/column_test.rb
index 4111a5f808..a7b63d15c9 100644
--- a/activerecord/test/cases/column_test.rb
+++ b/activerecord/test/cases/column_test.rb
@@ -76,6 +76,12 @@ module ActiveRecord
date_string = Time.now.utc.strftime("%F")
assert_equal date_string, column.type_cast(date_string).strftime("%F")
end
+
+ def test_type_cast_duration_to_integer
+ column = Column.new("field", nil, "integer")
+ assert_equal 1800, column.type_cast(30.minutes)
+ assert_equal 7200, column.type_cast(2.hours)
+ end
end
end
end
diff --git a/activerecord/test/cases/custom_locking_test.rb b/activerecord/test/cases/custom_locking_test.rb
index 42ef51ef3e..e8290297e3 100644
--- a/activerecord/test/cases/custom_locking_test.rb
+++ b/activerecord/test/cases/custom_locking_test.rb
@@ -9,7 +9,7 @@ module ActiveRecord
if current_adapter?(:MysqlAdapter, :Mysql2Adapter)
assert_match 'SHARE MODE', Person.lock('LOCK IN SHARE MODE').to_sql
assert_sql(/LOCK IN SHARE MODE/) do
- Person.scoped(:lock => 'LOCK IN SHARE MODE').find(1)
+ Person.all.merge!(:lock => 'LOCK IN SHARE MODE').find(1)
end
end
end
diff --git a/activerecord/test/cases/deprecated_dynamic_methods_test.rb b/activerecord/test/cases/deprecated_dynamic_methods_test.rb
index 09ca61aa1b..71bd8a776e 100644
--- a/activerecord/test/cases/deprecated_dynamic_methods_test.rb
+++ b/activerecord/test/cases/deprecated_dynamic_methods_test.rb
@@ -260,21 +260,21 @@ class DeprecatedDynamicMethodsTest < ActiveRecord::TestCase
def test_find_last_with_limit_gives_same_result_when_loaded_and_unloaded
scope = Topic.limit(2)
unloaded_last = scope.last
- loaded_last = scope.all.last
+ loaded_last = scope.to_a.last
assert_equal loaded_last, unloaded_last
end
def test_find_last_with_limit_and_offset_gives_same_result_when_loaded_and_unloaded
scope = Topic.offset(2).limit(2)
unloaded_last = scope.last
- loaded_last = scope.all.last
+ loaded_last = scope.to_a.last
assert_equal loaded_last, unloaded_last
end
def test_find_last_with_offset_gives_same_result_when_loaded_and_unloaded
scope = Topic.offset(3)
unloaded_last = scope.last
- loaded_last = scope.all.last
+ loaded_last = scope.to_a.last
assert_equal loaded_last, unloaded_last
end
@@ -292,17 +292,17 @@ class DeprecatedDynamicMethodsTest < ActiveRecord::TestCase
end
def test_dynamic_find_all_should_respect_association_order
- assert_equal [companies(:second_client), companies(:first_client)], companies(:first_firm).clients_sorted_desc.scoped(:where => "type = 'Client'").all
+ assert_equal [companies(:second_client), companies(:first_client)], companies(:first_firm).clients_sorted_desc.where("type = 'Client'").to_a
assert_equal [companies(:second_client), companies(:first_client)], companies(:first_firm).clients_sorted_desc.find_all_by_type('Client')
end
def test_dynamic_find_all_should_respect_association_limit
- assert_equal 1, companies(:first_firm).limited_clients.scoped(:where => "type = 'Client'").all.length
+ assert_equal 1, companies(:first_firm).limited_clients.where("type = 'Client'").to_a.length
assert_equal 1, companies(:first_firm).limited_clients.find_all_by_type('Client').length
end
def test_dynamic_find_all_limit_should_override_association_limit
- assert_equal 2, companies(:first_firm).limited_clients.scoped(:where => "type = 'Client'", :limit => 9_000).all.length
+ assert_equal 2, companies(:first_firm).limited_clients.where("type = 'Client'").limit(9_000).to_a.length
assert_equal 2, companies(:first_firm).limited_clients.find_all_by_type('Client', :limit => 9_000).length
end
@@ -320,22 +320,22 @@ class DeprecatedDynamicMethodsTest < ActiveRecord::TestCase
end
def test_dynamic_find_all_should_respect_association_order_for_through
- assert_equal [Comment.find(10), Comment.find(7), Comment.find(6), Comment.find(3)], authors(:david).comments_desc.scoped(:where => "comments.type = 'SpecialComment'").all
+ assert_equal [Comment.find(10), Comment.find(7), Comment.find(6), Comment.find(3)], authors(:david).comments_desc.where("comments.type = 'SpecialComment'").to_a
assert_equal [Comment.find(10), Comment.find(7), Comment.find(6), Comment.find(3)], authors(:david).comments_desc.find_all_by_type('SpecialComment')
end
def test_dynamic_find_all_should_respect_association_limit_for_through
- assert_equal 1, authors(:david).limited_comments.scoped(:where => "comments.type = 'SpecialComment'").all.length
+ assert_equal 1, authors(:david).limited_comments.where("comments.type = 'SpecialComment'").to_a.length
assert_equal 1, authors(:david).limited_comments.find_all_by_type('SpecialComment').length
end
def test_dynamic_find_all_order_should_override_association_limit_for_through
- assert_equal 4, authors(:david).limited_comments.scoped(:where => "comments.type = 'SpecialComment'", :limit => 9_000).all.length
+ assert_equal 4, authors(:david).limited_comments.where("comments.type = 'SpecialComment'").limit(9_000).to_a.length
assert_equal 4, authors(:david).limited_comments.find_all_by_type('SpecialComment', :limit => 9_000).length
end
def test_find_all_include_over_the_same_table_for_through
- assert_equal 2, people(:michael).posts.scoped(:includes => :people).all.length
+ assert_equal 2, people(:michael).posts.includes(:people).to_a.length
end
def test_find_or_create_by_resets_cached_counters
@@ -411,7 +411,7 @@ class DeprecatedDynamicMethodsTest < ActiveRecord::TestCase
end
def test_dynamic_find_all_by_attributes
- authors = Author.scoped
+ authors = Author.all
davids = authors.find_all_by_name('David')
assert_kind_of Array, davids
@@ -419,7 +419,7 @@ class DeprecatedDynamicMethodsTest < ActiveRecord::TestCase
end
def test_dynamic_find_or_initialize_by_attributes
- authors = Author.scoped
+ authors = Author.all
lifo = authors.find_or_initialize_by_name('Lifo')
assert_equal "Lifo", lifo.name
@@ -429,7 +429,7 @@ class DeprecatedDynamicMethodsTest < ActiveRecord::TestCase
end
def test_dynamic_find_or_create_by_attributes
- authors = Author.scoped
+ authors = Author.all
lifo = authors.find_or_create_by_name('Lifo')
assert_equal "Lifo", lifo.name
@@ -439,7 +439,7 @@ class DeprecatedDynamicMethodsTest < ActiveRecord::TestCase
end
def test_dynamic_find_or_create_by_attributes_bang
- authors = Author.scoped
+ authors = Author.all
assert_raises(ActiveRecord::RecordInvalid) { authors.find_or_create_by_name!('') }
@@ -504,7 +504,7 @@ class DynamicScopeTest < ActiveRecord::TestCase
def test_dynamic_scope
assert_equal @test_klass.scoped_by_author_id(1).find(1), @test_klass.find(1)
- assert_equal @test_klass.scoped_by_author_id_and_title(1, "Welcome to the weblog").first, @test_klass.scoped(:where => { :author_id => 1, :title => "Welcome to the weblog"}).first
+ assert_equal @test_klass.scoped_by_author_id_and_title(1, "Welcome to the weblog").first, @test_klass.all.merge!(:where => { :author_id => 1, :title => "Welcome to the weblog"}).first
end
def test_dynamic_scope_should_create_methods_after_hitting_method_missing
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index 97ffc068af..3f26e10dda 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -424,7 +424,7 @@ class DirtyTest < ActiveRecord::TestCase
with_partial_updates(Topic) do
Topic.create!(:author_name => 'Bill', :content => {:a => "a"})
topic = Topic.select('id, author_name').first
- topic.update_column :author_name, 'John'
+ topic.update_columns author_name: 'John'
topic = Topic.first
assert_not_nil topic.content
end
diff --git a/activerecord/test/cases/explain_test.rb b/activerecord/test/cases/explain_test.rb
index cb7781f8e7..52e747db35 100644
--- a/activerecord/test/cases/explain_test.rb
+++ b/activerecord/test/cases/explain_test.rb
@@ -20,7 +20,7 @@ if ActiveRecord::Base.connection.supports_explain?
end
with_threshold(0) do
- Car.where(:name => 'honda').all
+ Car.where(:name => 'honda').to_a
end
end
@@ -45,7 +45,7 @@ if ActiveRecord::Base.connection.supports_explain?
queries = Thread.current[:available_queries_for_explain] = []
with_threshold(0) do
- Car.where(:name => 'honda').all
+ Car.where(:name => 'honda').to_a
end
sql, binds = queries[0]
@@ -58,7 +58,7 @@ if ActiveRecord::Base.connection.supports_explain?
def test_collecting_queries_for_explain
result, queries = ActiveRecord::Base.collecting_queries_for_explain do
- Car.where(:name => 'honda').all
+ Car.where(:name => 'honda').to_a
end
sql, binds = queries[0]
@@ -68,6 +68,16 @@ if ActiveRecord::Base.connection.supports_explain?
assert_equal [cars(:honda)], result
end
+ def test_logging_query_plan_when_counting_by_sql
+ base.logger.expects(:warn).with do |message|
+ message.starts_with?('EXPLAIN for:')
+ end
+
+ with_threshold(0) do
+ Car.count_by_sql "SELECT COUNT(*) FROM cars WHERE name = 'honda'"
+ end
+ end
+
def test_exec_explain_with_no_binds
sqls = %w(foo bar)
binds = [[], []]
@@ -102,7 +112,7 @@ if ActiveRecord::Base.connection.supports_explain?
base.expects(:collecting_sqls_for_explain).never
base.logger.expects(:warn).never
base.silence_auto_explain do
- with_threshold(0) { Car.all }
+ with_threshold(0) { Car.to_a }
end
end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 576a455f09..d063afe61f 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -99,14 +99,14 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_by_ids_with_limit_and_offset
- assert_equal 2, Entrant.scoped(:limit => 2).find([1,3,2]).size
- assert_equal 1, Entrant.scoped(:limit => 3, :offset => 2).find([1,3,2]).size
+ assert_equal 2, Entrant.all.merge!(:limit => 2).find([1,3,2]).size
+ assert_equal 1, Entrant.all.merge!(:limit => 3, :offset => 2).find([1,3,2]).size
# Also test an edge case: If you have 11 results, and you set a
# limit of 3 and offset of 9, then you should find that there
# will be only 2 results, regardless of the limit.
- devs = Developer.all
- last_devs = Developer.scoped(:limit => 3, :offset => 9).find devs.map(&:id)
+ devs = Developer.to_a
+ last_devs = Developer.all.merge!(:limit => 3, :offset => 9).find devs.map(&:id)
assert_equal 2, last_devs.size
end
@@ -123,7 +123,7 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_with_group_and_sanitized_having_method
- developers = Developer.group(:salary).having("sum(salary) > ?", 10000).select('salary').all
+ developers = Developer.group(:salary).having("sum(salary) > ?", 10000).select('salary').to_a
assert_equal 3, developers.size
assert_equal 3, developers.map(&:salary).uniq.size
assert developers.all? { |developer| developer.salary > 10000 }
@@ -259,7 +259,7 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_only_some_columns
- topic = Topic.scoped(:select => "author_name").find(1)
+ topic = Topic.all.merge!(:select => "author_name").find(1)
assert_raise(ActiveModel::MissingAttributeError) {topic.title}
assert_nil topic.read_attribute("title")
assert_equal "David", topic.author_name
@@ -270,23 +270,23 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_on_array_conditions
- assert Topic.scoped(:where => ["approved = ?", false]).find(1)
- assert_raise(ActiveRecord::RecordNotFound) { Topic.scoped(:where => ["approved = ?", true]).find(1) }
+ assert Topic.all.merge!(:where => ["approved = ?", false]).find(1)
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.all.merge!(:where => ["approved = ?", true]).find(1) }
end
def test_find_on_hash_conditions
- assert Topic.scoped(:where => { :approved => false }).find(1)
- assert_raise(ActiveRecord::RecordNotFound) { Topic.scoped(:where => { :approved => true }).find(1) }
+ assert Topic.all.merge!(:where => { :approved => false }).find(1)
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.all.merge!(:where => { :approved => true }).find(1) }
end
def test_find_on_hash_conditions_with_explicit_table_name
- assert Topic.scoped(:where => { 'topics.approved' => false }).find(1)
- assert_raise(ActiveRecord::RecordNotFound) { Topic.scoped(:where => { 'topics.approved' => true }).find(1) }
+ assert Topic.all.merge!(:where => { 'topics.approved' => false }).find(1)
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.all.merge!(:where => { 'topics.approved' => true }).find(1) }
end
def test_find_on_hash_conditions_with_hashed_table_name
- assert Topic.scoped(:where => {:topics => { :approved => false }}).find(1)
- assert_raise(ActiveRecord::RecordNotFound) { Topic.scoped(:where => {:topics => { :approved => true }}).find(1) }
+ assert Topic.all.merge!(:where => {:topics => { :approved => false }}).find(1)
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.all.merge!(:where => {:topics => { :approved => true }}).find(1) }
end
def test_find_with_hash_conditions_on_joined_table
@@ -296,7 +296,7 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_with_hash_conditions_on_joined_table_and_with_range
- firms = DependentFirm.scoped :joins => :account, :where => {:name => 'RailsCore', :accounts => { :credit_limit => 55..60 }}
+ firms = DependentFirm.all.merge!(:joins => :account, :where => {:name => 'RailsCore', :accounts => { :credit_limit => 55..60 }})
assert_equal 1, firms.size
assert_equal companies(:rails_core), firms.first
end
@@ -306,71 +306,71 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_on_hash_conditions_with_range
- assert_equal [1,2], Topic.scoped(:where => { :id => 1..2 }).all.map(&:id).sort
- assert_raise(ActiveRecord::RecordNotFound) { Topic.scoped(:where => { :id => 2..3 }).find(1) }
+ assert_equal [1,2], Topic.all.merge!(:where => { :id => 1..2 }).to_a.map(&:id).sort
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.all.merge!(:where => { :id => 2..3 }).find(1) }
end
def test_find_on_hash_conditions_with_end_exclusive_range
- assert_equal [1,2,3], Topic.scoped(:where => { :id => 1..3 }).all.map(&:id).sort
- assert_equal [1,2], Topic.scoped(:where => { :id => 1...3 }).all.map(&:id).sort
- assert_raise(ActiveRecord::RecordNotFound) { Topic.scoped(:where => { :id => 2...3 }).find(3) }
+ assert_equal [1,2,3], Topic.all.merge!(:where => { :id => 1..3 }).to_a.map(&:id).sort
+ assert_equal [1,2], Topic.all.merge!(:where => { :id => 1...3 }).to_a.map(&:id).sort
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.all.merge!(:where => { :id => 2...3 }).find(3) }
end
def test_find_on_hash_conditions_with_multiple_ranges
- assert_equal [1,2,3], Comment.scoped(:where => { :id => 1..3, :post_id => 1..2 }).all.map(&:id).sort
- assert_equal [1], Comment.scoped(:where => { :id => 1..1, :post_id => 1..10 }).all.map(&:id).sort
+ assert_equal [1,2,3], Comment.all.merge!(:where => { :id => 1..3, :post_id => 1..2 }).to_a.map(&:id).sort
+ assert_equal [1], Comment.all.merge!(:where => { :id => 1..1, :post_id => 1..10 }).to_a.map(&:id).sort
end
def test_find_on_hash_conditions_with_array_of_integers_and_ranges
- assert_equal [1,2,3,5,6,7,8,9], Comment.scoped(:where => {:id => [1..2, 3, 5, 6..8, 9]}).all.map(&:id).sort
+ assert_equal [1,2,3,5,6,7,8,9], Comment.all.merge!(:where => {:id => [1..2, 3, 5, 6..8, 9]}).to_a.map(&:id).sort
end
def test_find_on_multiple_hash_conditions
- assert Topic.scoped(:where => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => false }).find(1)
- assert_raise(ActiveRecord::RecordNotFound) { Topic.scoped(:where => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }).find(1) }
- assert_raise(ActiveRecord::RecordNotFound) { Topic.scoped(:where => { :author_name => "David", :title => "HHC", :replies_count => 1, :approved => false }).find(1) }
- assert_raise(ActiveRecord::RecordNotFound) { Topic.scoped(:where => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }).find(1) }
+ assert Topic.all.merge!(:where => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => false }).find(1)
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.all.merge!(:where => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }).find(1) }
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.all.merge!(:where => { :author_name => "David", :title => "HHC", :replies_count => 1, :approved => false }).find(1) }
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.all.merge!(:where => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }).find(1) }
end
def test_condition_interpolation
assert_kind_of Firm, Company.where("name = '%s'", "37signals").first
- assert_nil Company.scoped(:where => ["name = '%s'", "37signals!"]).first
- assert_nil Company.scoped(:where => ["name = '%s'", "37signals!' OR 1=1"]).first
- assert_kind_of Time, Topic.scoped(:where => ["id = %d", 1]).first.written_on
+ assert_nil Company.all.merge!(:where => ["name = '%s'", "37signals!"]).first
+ assert_nil Company.all.merge!(:where => ["name = '%s'", "37signals!' OR 1=1"]).first
+ assert_kind_of Time, Topic.all.merge!(:where => ["id = %d", 1]).first.written_on
end
def test_condition_array_interpolation
- assert_kind_of Firm, Company.scoped(:where => ["name = '%s'", "37signals"]).first
- assert_nil Company.scoped(:where => ["name = '%s'", "37signals!"]).first
- assert_nil Company.scoped(:where => ["name = '%s'", "37signals!' OR 1=1"]).first
- assert_kind_of Time, Topic.scoped(:where => ["id = %d", 1]).first.written_on
+ assert_kind_of Firm, Company.all.merge!(:where => ["name = '%s'", "37signals"]).first
+ assert_nil Company.all.merge!(:where => ["name = '%s'", "37signals!"]).first
+ assert_nil Company.all.merge!(:where => ["name = '%s'", "37signals!' OR 1=1"]).first
+ assert_kind_of Time, Topic.all.merge!(:where => ["id = %d", 1]).first.written_on
end
def test_condition_hash_interpolation
- assert_kind_of Firm, Company.scoped(:where => { :name => "37signals"}).first
- assert_nil Company.scoped(:where => { :name => "37signals!"}).first
- assert_kind_of Time, Topic.scoped(:where => {:id => 1}).first.written_on
+ assert_kind_of Firm, Company.all.merge!(:where => { :name => "37signals"}).first
+ assert_nil Company.all.merge!(:where => { :name => "37signals!"}).first
+ assert_kind_of Time, Topic.all.merge!(:where => {:id => 1}).first.written_on
end
def test_hash_condition_find_malformed
assert_raise(ActiveRecord::StatementInvalid) {
- Company.scoped(:where => { :id => 2, :dhh => true }).first
+ Company.all.merge!(:where => { :id => 2, :dhh => true }).first
}
end
def test_hash_condition_find_with_escaped_characters
Company.create("name" => "Ain't noth'n like' \#stuff")
- assert Company.scoped(:where => { :name => "Ain't noth'n like' \#stuff" }).first
+ assert Company.all.merge!(:where => { :name => "Ain't noth'n like' \#stuff" }).first
end
def test_hash_condition_find_with_array
- p1, p2 = Post.scoped(:limit => 2, :order => 'id asc').all
- assert_equal [p1, p2], Post.scoped(:where => { :id => [p1, p2] }, :order => 'id asc').all
- assert_equal [p1, p2], Post.scoped(:where => { :id => [p1, p2.id] }, :order => 'id asc').all
+ p1, p2 = Post.all.merge!(:limit => 2, :order => 'id asc').to_a
+ assert_equal [p1, p2], Post.all.merge!(:where => { :id => [p1, p2] }, :order => 'id asc').to_a
+ assert_equal [p1, p2], Post.all.merge!(:where => { :id => [p1, p2.id] }, :order => 'id asc').to_a
end
def test_hash_condition_find_with_nil
- topic = Topic.scoped(:where => { :last_read => nil } ).first
+ topic = Topic.all.merge!(:where => { :last_read => nil } ).first
assert_not_nil topic
assert_nil topic.last_read
end
@@ -379,7 +379,7 @@ class FinderTest < ActiveRecord::TestCase
with_env_tz 'America/New_York' do
with_active_record_default_timezone :local do
topic = Topic.first
- assert_equal topic, Topic.scoped(:where => ['written_on = ?', topic.written_on.getutc]).first
+ assert_equal topic, Topic.all.merge!(:where => ['written_on = ?', topic.written_on.getutc]).first
end
end
end
@@ -388,7 +388,7 @@ class FinderTest < ActiveRecord::TestCase
with_env_tz 'America/New_York' do
with_active_record_default_timezone :local do
topic = Topic.first
- assert_equal topic, Topic.scoped(:where => {:written_on => topic.written_on.getutc}).first
+ assert_equal topic, Topic.all.merge!(:where => {:written_on => topic.written_on.getutc}).first
end
end
end
@@ -397,7 +397,7 @@ class FinderTest < ActiveRecord::TestCase
with_env_tz 'America/New_York' do
with_active_record_default_timezone :utc do
topic = Topic.first
- assert_equal topic, Topic.scoped(:where => ['written_on = ?', topic.written_on.getlocal]).first
+ assert_equal topic, Topic.all.merge!(:where => ['written_on = ?', topic.written_on.getlocal]).first
end
end
end
@@ -406,32 +406,32 @@ class FinderTest < ActiveRecord::TestCase
with_env_tz 'America/New_York' do
with_active_record_default_timezone :utc do
topic = Topic.first
- assert_equal topic, Topic.scoped(:where => {:written_on => topic.written_on.getlocal}).first
+ assert_equal topic, Topic.all.merge!(:where => {:written_on => topic.written_on.getlocal}).first
end
end
end
def test_bind_variables
- assert_kind_of Firm, Company.scoped(:where => ["name = ?", "37signals"]).first
- assert_nil Company.scoped(:where => ["name = ?", "37signals!"]).first
- assert_nil Company.scoped(:where => ["name = ?", "37signals!' OR 1=1"]).first
- assert_kind_of Time, Topic.scoped(:where => ["id = ?", 1]).first.written_on
+ assert_kind_of Firm, Company.all.merge!(:where => ["name = ?", "37signals"]).first
+ assert_nil Company.all.merge!(:where => ["name = ?", "37signals!"]).first
+ assert_nil Company.all.merge!(:where => ["name = ?", "37signals!' OR 1=1"]).first
+ assert_kind_of Time, Topic.all.merge!(:where => ["id = ?", 1]).first.written_on
assert_raise(ActiveRecord::PreparedStatementInvalid) {
- Company.scoped(:where => ["id=? AND name = ?", 2]).first
+ Company.all.merge!(:where => ["id=? AND name = ?", 2]).first
}
assert_raise(ActiveRecord::PreparedStatementInvalid) {
- Company.scoped(:where => ["id=?", 2, 3, 4]).first
+ Company.all.merge!(:where => ["id=?", 2, 3, 4]).first
}
end
def test_bind_variables_with_quotes
Company.create("name" => "37signals' go'es agains")
- assert Company.scoped(:where => ["name = ?", "37signals' go'es agains"]).first
+ assert Company.all.merge!(:where => ["name = ?", "37signals' go'es agains"]).first
end
def test_named_bind_variables_with_quotes
Company.create("name" => "37signals' go'es agains")
- assert Company.scoped(:where => ["name = :name", {:name => "37signals' go'es agains"}]).first
+ assert Company.all.merge!(:where => ["name = :name", {:name => "37signals' go'es agains"}]).first
end
def test_bind_arity
@@ -449,10 +449,10 @@ class FinderTest < ActiveRecord::TestCase
assert_nothing_raised { bind("'+00:00'", :foo => "bar") }
- assert_kind_of Firm, Company.scoped(:where => ["name = :name", { :name => "37signals" }]).first
- assert_nil Company.scoped(:where => ["name = :name", { :name => "37signals!" }]).first
- assert_nil Company.scoped(:where => ["name = :name", { :name => "37signals!' OR 1=1" }]).first
- assert_kind_of Time, Topic.scoped(:where => ["id = :id", { :id => 1 }]).first.written_on
+ assert_kind_of Firm, Company.all.merge!(:where => ["name = :name", { :name => "37signals" }]).first
+ assert_nil Company.all.merge!(:where => ["name = :name", { :name => "37signals!" }]).first
+ assert_nil Company.all.merge!(:where => ["name = :name", { :name => "37signals!' OR 1=1" }]).first
+ assert_kind_of Time, Topic.all.merge!(:where => ["id = :id", { :id => 1 }]).first.written_on
end
class SimpleEnumerable
@@ -595,10 +595,10 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_all_with_join
- developers_on_project_one = Developer.scoped(
+ developers_on_project_one = Developer.all.merge!(
:joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
:where => 'project_id=1'
- ).all
+ ).to_a
assert_equal 3, developers_on_project_one.length
developer_names = developers_on_project_one.map { |d| d.name }
assert developer_names.include?('David')
@@ -606,7 +606,7 @@ class FinderTest < ActiveRecord::TestCase
end
def test_joins_dont_clobber_id
- first = Firm.scoped(
+ first = Firm.all.merge!(
:joins => 'INNER JOIN companies clients ON clients.firm_id = companies.id',
:where => 'companies.id = 1'
).first
@@ -614,7 +614,7 @@ class FinderTest < ActiveRecord::TestCase
end
def test_joins_with_string_array
- person_with_reader_and_post = Post.scoped(
+ person_with_reader_and_post = Post.all.merge!(
:joins => [
"INNER JOIN categorizations ON categorizations.post_id = posts.id",
"INNER JOIN categories ON categories.id = categorizations.category_id AND categories.type = 'SpecialCategory'"
@@ -644,9 +644,9 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_by_records
- p1, p2 = Post.scoped(:limit => 2, :order => 'id asc').all
- assert_equal [p1, p2], Post.scoped(:where => ['id in (?)', [p1, p2]], :order => 'id asc')
- assert_equal [p1, p2], Post.scoped(:where => ['id in (?)', [p1, p2.id]], :order => 'id asc')
+ p1, p2 = Post.all.merge!(:limit => 2, :order => 'id asc').to_a
+ assert_equal [p1, p2], Post.all.merge!(:where => ['id in (?)', [p1, p2]], :order => 'id asc')
+ assert_equal [p1, p2], Post.all.merge!(:where => ['id in (?)', [p1, p2.id]], :order => 'id asc')
end
def test_select_value
@@ -673,14 +673,14 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct
- assert_equal 2, Post.scoped(:includes => { :authors => :author_address }, :order => 'author_addresses.id DESC ', :limit => 2).all.size
+ assert_equal 2, Post.all.merge!(:includes => { :authors => :author_address }, :order => 'author_addresses.id DESC ', :limit => 2).to_a.size
- assert_equal 3, Post.scoped(:includes => { :author => :author_address, :authors => :author_address},
- :order => 'author_addresses_authors.id DESC ', :limit => 3).all.size
+ assert_equal 3, Post.all.merge!(:includes => { :author => :author_address, :authors => :author_address},
+ :order => 'author_addresses_authors.id DESC ', :limit => 3).to_a.size
end
def test_find_with_nil_inside_set_passed_for_one_attribute
- client_of = Company.scoped(
+ client_of = Company.all.merge!(
:where => {
:client_of => [2, 1, nil],
:name => ['37signals', 'Summit', 'Microsoft'] },
@@ -692,7 +692,7 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_with_nil_inside_set_passed_for_attribute
- client_of = Company.scoped(
+ client_of = Company.all.merge!(
:where => { :client_of => [nil] },
:order => 'client_of DESC'
).map { |x| x.client_of }
@@ -701,10 +701,10 @@ class FinderTest < ActiveRecord::TestCase
end
def test_with_limiting_with_custom_select
- posts = Post.references(:authors).scoped(
+ posts = Post.references(:authors).merge(
:includes => :author, :select => ' posts.*, authors.id as "author_id"',
:limit => 3, :order => 'posts.id'
- ).all
+ ).to_a
assert_equal 3, posts.size
assert_equal [0, 1, 1], posts.map(&:author_id).sort
end
@@ -719,7 +719,7 @@ class FinderTest < ActiveRecord::TestCase
end
def test_finder_with_offset_string
- assert_nothing_raised(ActiveRecord::StatementInvalid) { Topic.scoped(:offset => "3").all }
+ assert_nothing_raised(ActiveRecord::StatementInvalid) { Topic.all.merge!(:offset => "3").to_a }
end
protected
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index afff020561..44d08a8ee4 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -5,7 +5,6 @@ require 'config'
gem 'minitest'
require 'minitest/autorun'
require 'stringio'
-require 'mocha'
require 'cases/test_case'
require 'active_record'
diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb
index 06de51f5cd..b14f580c77 100644
--- a/activerecord/test/cases/inheritance_test.rb
+++ b/activerecord/test/cases/inheritance_test.rb
@@ -1,7 +1,10 @@
require "cases/helper"
require 'models/company'
+require 'models/person'
+require 'models/post'
require 'models/project'
require 'models/subscriber'
+require 'models/teapot'
class InheritanceTest < ActiveRecord::TestCase
fixtures :companies, :projects, :subscribers, :accounts
@@ -24,7 +27,7 @@ class InheritanceTest < ActiveRecord::TestCase
end
})
company.save!
- company = Company.all.find { |x| x.id == company.id }
+ company = Company.to_a.find { |x| x.id == company.id }
assert_equal ' ', company.type
end
@@ -70,6 +73,33 @@ class InheritanceTest < ActiveRecord::TestCase
assert !Class.new(Company).descends_from_active_record?, 'Company subclass should not descend from ActiveRecord::Base'
end
+ def test_inheritance_base_class
+ assert_equal Post, Post.base_class
+ assert_equal Post, SpecialPost.base_class
+ assert_equal Post, StiPost.base_class
+ assert_equal SubStiPost, SubStiPost.base_class
+ end
+
+ def test_active_record_model_included_base_class
+ assert_equal Teapot, Teapot.base_class
+ end
+
+ def test_abstract_inheritance_base_class
+ assert_equal LoosePerson, LoosePerson.base_class
+ assert_equal LooseDescendant, LooseDescendant.base_class
+ assert_equal TightPerson, TightPerson.base_class
+ assert_equal TightPerson, TightDescendant.base_class
+ end
+
+ def test_base_class_activerecord_error
+ klass = Class.new {
+ extend ActiveRecord::Configuration
+ include ActiveRecord::Inheritance
+ }
+
+ assert_raise(ActiveRecord::ActiveRecordError) { klass.base_class }
+ end
+
def test_a_bad_type_column
#SQLServer need to turn Identity Insert On before manually inserting into the Identity column
if current_adapter?(:SybaseAdapter)
@@ -98,7 +128,7 @@ class InheritanceTest < ActiveRecord::TestCase
end
def test_inheritance_find_all
- companies = Company.scoped(:order => 'id').all
+ companies = Company.all.merge!(:order => 'id').to_a
assert_kind_of Firm, companies[0], "37signals should be a firm"
assert_kind_of Client, companies[1], "Summit should be a client"
end
@@ -149,9 +179,9 @@ class InheritanceTest < ActiveRecord::TestCase
def test_update_all_within_inheritance
Client.update_all "name = 'I am a client'"
- assert_equal "I am a client", Client.all.first.name
+ assert_equal "I am a client", Client.to_a.first.name
# Order by added as otherwise Oracle tests were failing because of different order of results
- assert_equal "37signals", Firm.scoped(:order => "id").all.first.name
+ assert_equal "37signals", Firm.all.merge!(:order => "id").to_a.first.name
end
def test_alt_update_all_within_inheritance
@@ -173,9 +203,9 @@ class InheritanceTest < ActiveRecord::TestCase
end
def test_find_first_within_inheritance
- assert_kind_of Firm, Company.scoped(:where => "name = '37signals'").first
- assert_kind_of Firm, Firm.scoped(:where => "name = '37signals'").first
- assert_nil Client.scoped(:where => "name = '37signals'").first
+ assert_kind_of Firm, Company.all.merge!(:where => "name = '37signals'").first
+ assert_kind_of Firm, Firm.all.merge!(:where => "name = '37signals'").first
+ assert_nil Client.all.merge!(:where => "name = '37signals'").first
end
def test_alt_find_first_within_inheritance
@@ -187,10 +217,10 @@ class InheritanceTest < ActiveRecord::TestCase
def test_complex_inheritance
very_special_client = VerySpecialClient.create("name" => "veryspecial")
assert_equal very_special_client, VerySpecialClient.where("name = 'veryspecial'").first
- assert_equal very_special_client, SpecialClient.scoped(:where => "name = 'veryspecial'").first
- assert_equal very_special_client, Company.scoped(:where => "name = 'veryspecial'").first
- assert_equal very_special_client, Client.scoped(:where => "name = 'veryspecial'").first
- assert_equal 1, Client.scoped(:where => "name = 'Summit'").all.size
+ assert_equal very_special_client, SpecialClient.all.merge!(:where => "name = 'veryspecial'").first
+ assert_equal very_special_client, Company.all.merge!(:where => "name = 'veryspecial'").first
+ assert_equal very_special_client, Client.all.merge!(:where => "name = 'veryspecial'").first
+ assert_equal 1, Client.all.merge!(:where => "name = 'Summit'").to_a.size
assert_equal very_special_client, Client.find(very_special_client.id)
end
@@ -201,14 +231,14 @@ class InheritanceTest < ActiveRecord::TestCase
end
def test_eager_load_belongs_to_something_inherited
- account = Account.scoped(:includes => :firm).find(1)
+ account = Account.all.merge!(:includes => :firm).find(1)
assert account.association_cache.key?(:firm), "nil proves eager load failed"
end
def test_eager_load_belongs_to_primary_key_quoting
con = Account.connection
assert_sql(/#{con.quote_table_name('companies')}.#{con.quote_column_name('id')} IN \(1\)/) do
- Account.scoped(:includes => :firm).find(1)
+ Account.all.merge!(:includes => :firm).find(1)
end
end
@@ -230,7 +260,7 @@ class InheritanceTest < ActiveRecord::TestCase
private
def switch_to_alt_inheritance_column
# we don't want misleading test results, so get rid of the values in the type column
- Company.scoped(:order => 'id').all.each do |c|
+ Company.all.merge!(:order => 'id').to_a.each do |c|
c['type'] = nil
c.save
end
diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb
index acd2fcdad4..049a57d8b9 100644
--- a/activerecord/test/cases/log_subscriber_test.rb
+++ b/activerecord/test/cases/log_subscriber_test.rb
@@ -54,7 +54,7 @@ class LogSubscriberTest < ActiveRecord::TestCase
end
def test_basic_query_logging
- Developer.all
+ Developer.to_a
wait
assert_equal 1, @logger.logged(:debug).size
assert_match(/Developer Load/, @logger.logged(:debug).last)
@@ -71,8 +71,8 @@ class LogSubscriberTest < ActiveRecord::TestCase
def test_cached_queries
ActiveRecord::Base.cache do
- Developer.all
- Developer.all
+ Developer.to_a
+ Developer.to_a
end
wait
assert_equal 2, @logger.logged(:debug).size
@@ -82,7 +82,7 @@ class LogSubscriberTest < ActiveRecord::TestCase
def test_basic_query_doesnt_log_when_level_is_not_debug
@logger.level = INFO
- Developer.all
+ Developer.to_a
wait
assert_equal 0, @logger.logged(:debug).size
end
@@ -90,8 +90,8 @@ class LogSubscriberTest < ActiveRecord::TestCase
def test_cached_queries_doesnt_log_when_level_is_not_debug
@logger.level = INFO
ActiveRecord::Base.cache do
- Developer.all
- Developer.all
+ Developer.to_a
+ Developer.to_a
end
wait
assert_equal 0, @logger.logged(:debug).size
diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb
index ce9be66069..ec4c554abb 100644
--- a/activerecord/test/cases/migration/change_schema_test.rb
+++ b/activerecord/test/cases/migration/change_schema_test.rb
@@ -141,8 +141,8 @@ module ActiveRecord
created_at_column = created_columns.detect {|c| c.name == 'created_at' }
updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
- assert !created_at_column.null
- assert !updated_at_column.null
+ assert created_at_column.null
+ assert updated_at_column.null
end
def test_create_table_with_timestamps_should_create_datetime_columns_with_options
diff --git a/activerecord/test/cases/migration/change_table_test.rb b/activerecord/test/cases/migration/change_table_test.rb
index 063209389f..4614be9650 100644
--- a/activerecord/test/cases/migration/change_table_test.rb
+++ b/activerecord/test/cases/migration/change_table_test.rb
@@ -30,61 +30,57 @@ module ActiveRecord
def test_references_column_type_adds_id
with_change_table do |t|
- @connection.expect :add_column, nil, [:delete_me, 'customer_id', :integer, {}]
+ @connection.expect :add_reference, nil, [:delete_me, :customer, {}]
t.references :customer
end
end
def test_remove_references_column_type_removes_id
with_change_table do |t|
- @connection.expect :remove_column, nil, [:delete_me, 'customer_id']
+ @connection.expect :remove_reference, nil, [:delete_me, :customer, {}]
t.remove_references :customer
end
end
def test_add_belongs_to_works_like_add_references
with_change_table do |t|
- @connection.expect :add_column, nil, [:delete_me, 'customer_id', :integer, {}]
+ @connection.expect :add_reference, nil, [:delete_me, :customer, {}]
t.belongs_to :customer
end
end
def test_remove_belongs_to_works_like_remove_references
with_change_table do |t|
- @connection.expect :remove_column, nil, [:delete_me, 'customer_id']
+ @connection.expect :remove_reference, nil, [:delete_me, :customer, {}]
t.remove_belongs_to :customer
end
end
def test_references_column_type_with_polymorphic_adds_type
with_change_table do |t|
- @connection.expect :add_column, nil, [:delete_me, 'taggable_id', :integer, {}]
- @connection.expect :add_column, nil, [:delete_me, 'taggable_type', :string, {}]
- t.references :taggable, :polymorphic => true
+ @connection.expect :add_reference, nil, [:delete_me, :taggable, polymorphic: true]
+ t.references :taggable, polymorphic: true
end
end
def test_remove_references_column_type_with_polymorphic_removes_type
with_change_table do |t|
- @connection.expect :remove_column, nil, [:delete_me, 'taggable_id']
- @connection.expect :remove_column, nil, [:delete_me, 'taggable_type']
- t.remove_references :taggable, :polymorphic => true
+ @connection.expect :remove_reference, nil, [:delete_me, :taggable, polymorphic: true]
+ t.remove_references :taggable, polymorphic: true
end
end
def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag
with_change_table do |t|
- @connection.expect :add_column, nil, [:delete_me, 'taggable_id', :integer, {:null => false}]
- @connection.expect :add_column, nil, [:delete_me, 'taggable_type', :string, {:null => false}]
- t.references :taggable, :polymorphic => true, :null => false
+ @connection.expect :add_reference, nil, [:delete_me, :taggable, polymorphic: true, null: false]
+ t.references :taggable, polymorphic: true, null: false
end
end
def test_remove_references_column_type_with_polymorphic_and_options_null_is_false_removes_table_flag
with_change_table do |t|
- @connection.expect :remove_column, nil, [:delete_me, 'taggable_id']
- @connection.expect :remove_column, nil, [:delete_me, 'taggable_type']
- t.remove_references :taggable, :polymorphic => true, :null => false
+ @connection.expect :remove_reference, nil, [:delete_me, :taggable, polymorphic: true, null: false]
+ t.remove_references :taggable, polymorphic: true, null: false
end
end
diff --git a/activerecord/test/cases/migration/column_attributes_test.rb b/activerecord/test/cases/migration/column_attributes_test.rb
index 3014bbe273..9584d5dd06 100644
--- a/activerecord/test/cases/migration/column_attributes_test.rb
+++ b/activerecord/test/cases/migration/column_attributes_test.rb
@@ -54,13 +54,13 @@ module ActiveRecord
# Do a manual insertion
if current_adapter?(:OracleAdapter)
- connection.execute "insert into test_models (id, wealth, created_at, updated_at) values (people_seq.nextval, 12345678901234567890.0123456789, sysdate, sysdate)"
+ connection.execute "insert into test_models (id, wealth) values (people_seq.nextval, 12345678901234567890.0123456789)"
elsif current_adapter?(:OpenBaseAdapter) || (current_adapter?(:MysqlAdapter) && Mysql.client_version < 50003) #before mysql 5.0.3 decimals stored as strings
- connection.execute "insert into test_models (wealth, created_at, updated_at) values ('12345678901234567890.0123456789', 0, 0)"
+ connection.execute "insert into test_models (wealth) values ('12345678901234567890.0123456789')"
elsif current_adapter?(:PostgreSQLAdapter)
- connection.execute "insert into test_models (wealth, created_at, updated_at) values (12345678901234567890.0123456789, now(), now())"
+ connection.execute "insert into test_models (wealth) values (12345678901234567890.0123456789)"
else
- connection.execute "insert into test_models (wealth, created_at, updated_at) values (12345678901234567890.0123456789, 0, 0)"
+ connection.execute "insert into test_models (wealth) values (12345678901234567890.0123456789)"
end
# SELECT
diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb
index 7d026961be..f2213ee6aa 100644
--- a/activerecord/test/cases/migration/command_recorder_test.rb
+++ b/activerecord/test/cases/migration/command_recorder_test.rb
@@ -110,9 +110,9 @@ module ActiveRecord
end
def test_invert_add_index_with_name
- @recorder.record :add_index, [:table, [:one, :two], {:name => "new_index"}]
- remove = @recorder.inverse.first
- assert_equal [:remove_index, [:table, {:name => "new_index"}]], remove
+ @recorder.record :add_index, [:table, [:one, :two], {:name => "new_index"}]
+ remove = @recorder.inverse.first
+ assert_equal [:remove_index, [:table, {:name => "new_index"}]], remove
end
def test_invert_add_index_with_no_options
@@ -138,6 +138,30 @@ module ActiveRecord
add = @recorder.inverse.first
assert_equal [:add_timestamps, [:table]], add
end
+
+ def test_invert_add_reference
+ @recorder.record :add_reference, [:table, :taggable, { polymorphic: true }]
+ remove = @recorder.inverse.first
+ assert_equal [:remove_reference, [:table, :taggable, { polymorphic: true }]], remove
+ end
+
+ def test_invert_add_belongs_to_alias
+ @recorder.record :add_belongs_to, [:table, :user]
+ remove = @recorder.inverse.first
+ assert_equal [:remove_reference, [:table, :user]], remove
+ end
+
+ def test_invert_remove_reference
+ @recorder.record :remove_reference, [:table, :taggable, { polymorphic: true }]
+ add = @recorder.inverse.first
+ assert_equal [:add_reference, [:table, :taggable, { polymorphic: true }]], add
+ end
+
+ def test_invert_remove_belongs_to_alias
+ @recorder.record :remove_belongs_to, [:table, :user]
+ add = @recorder.inverse.first
+ assert_equal [:add_reference, [:table, :user]], add
+ end
end
end
end
diff --git a/activerecord/test/cases/migration/create_join_table_test.rb b/activerecord/test/cases/migration/create_join_table_test.rb
index 8b91b3bc92..cd1b0e8b47 100644
--- a/activerecord/test/cases/migration/create_join_table_test.rb
+++ b/activerecord/test/cases/migration/create_join_table_test.rb
@@ -42,22 +42,36 @@ module ActiveRecord
end
def test_create_join_table_with_the_table_name
- connection.create_join_table :artists, :musics, :table_name => :catalog
+ connection.create_join_table :artists, :musics, table_name: :catalog
assert_equal %w(artist_id music_id), connection.columns(:catalog).map(&:name).sort
end
def test_create_join_table_with_the_table_name_as_string
- connection.create_join_table :artists, :musics, :table_name => 'catalog'
+ connection.create_join_table :artists, :musics, table_name: 'catalog'
assert_equal %w(artist_id music_id), connection.columns(:catalog).map(&:name).sort
end
def test_create_join_table_with_column_options
- connection.create_join_table :artists, :musics, :column_options => {:null => true}
+ connection.create_join_table :artists, :musics, column_options: {null: true}
assert_equal [true, true], connection.columns(:artists_musics).map(&:null)
end
+
+ def test_create_join_table_without_indexes
+ connection.create_join_table :artists, :musics
+
+ assert connection.indexes(:artists_musics).blank?
+ end
+
+ def test_create_join_table_with_index
+ connection.create_join_table :artists, :musics do |t|
+ t.index [:artist_id, :music_id]
+ end
+
+ assert_equal [%w(artist_id music_id)], connection.indexes(:artists_musics).map(&:columns)
+ end
end
end
end
diff --git a/activerecord/test/cases/migration/helper.rb b/activerecord/test/cases/migration/helper.rb
index 20f26786f0..768ebc5861 100644
--- a/activerecord/test/cases/migration/helper.rb
+++ b/activerecord/test/cases/migration/helper.rb
@@ -14,7 +14,7 @@ module ActiveRecord
module TestHelper
attr_reader :connection, :table_name
- CONNECTION_METHODS = %w[add_column remove_column rename_column add_index change_column rename_table]
+ CONNECTION_METHODS = %w[add_column remove_column rename_column add_index change_column rename_table column_exists? index_exists? add_reference add_belongs_to remove_reference remove_references remove_belongs_to]
class TestModel < ActiveRecord::Base
self.table_name = :test_models
diff --git a/activerecord/test/cases/migration/references_statements_test.rb b/activerecord/test/cases/migration/references_statements_test.rb
new file mode 100644
index 0000000000..144302bd4a
--- /dev/null
+++ b/activerecord/test/cases/migration/references_statements_test.rb
@@ -0,0 +1,111 @@
+require "cases/migration/helper"
+
+module ActiveRecord
+ class Migration
+ class ReferencesStatementsTest < ActiveRecord::TestCase
+ include ActiveRecord::Migration::TestHelper
+
+ self.use_transactional_fixtures = false
+
+ def setup
+ super
+ @table_name = :test_models
+
+ add_column table_name, :supplier_id, :integer
+ add_index table_name, :supplier_id
+ end
+
+ def test_creates_reference_id_column
+ add_reference table_name, :user
+ assert column_exists?(table_name, :user_id, :integer)
+ end
+
+ def test_does_not_create_reference_type_column
+ add_reference table_name, :taggable
+ refute column_exists?(table_name, :taggable_type, :string)
+ end
+
+ def test_creates_reference_type_column
+ add_reference table_name, :taggable, polymorphic: true
+ assert column_exists?(table_name, :taggable_type, :string)
+ end
+
+ def test_creates_reference_id_index
+ add_reference table_name, :user, index: true
+ assert index_exists?(table_name, :user_id)
+ end
+
+ def test_does_not_create_reference_id_index
+ add_reference table_name, :user
+ refute index_exists?(table_name, :user_id)
+ end
+
+ def test_creates_polymorphic_index
+ add_reference table_name, :taggable, polymorphic: true, index: true
+ assert index_exists?(table_name, [:taggable_id, :taggable_type])
+ end
+
+ def test_creates_reference_type_column_with_default
+ add_reference table_name, :taggable, polymorphic: { default: 'Photo' }, index: true
+ assert column_exists?(table_name, :taggable_type, :string, default: 'Photo')
+ end
+
+ def test_creates_named_index
+ add_reference table_name, :tag, index: { name: 'index_taggings_on_tag_id' }
+ assert index_exists?(table_name, :tag_id, name: 'index_taggings_on_tag_id')
+ end
+
+ def test_deletes_reference_id_column
+ remove_reference table_name, :supplier
+ refute column_exists?(table_name, :supplier_id, :integer)
+ end
+
+ def test_deletes_reference_id_index
+ remove_reference table_name, :supplier
+ refute index_exists?(table_name, :supplier_id)
+ end
+
+ def test_does_not_delete_reference_type_column
+ with_polymorphic_column do
+ remove_reference table_name, :supplier
+
+ refute column_exists?(table_name, :supplier_id, :integer)
+ assert column_exists?(table_name, :supplier_type, :string)
+ end
+ end
+
+ def test_deletes_reference_type_column
+ with_polymorphic_column do
+ remove_reference table_name, :supplier, polymorphic: true
+ refute column_exists?(table_name, :supplier_type, :string)
+ end
+ end
+
+ def test_deletes_polymorphic_index
+ with_polymorphic_column do
+ remove_reference table_name, :supplier, polymorphic: true
+ refute index_exists?(table_name, [:supplier_id, :supplier_type])
+ end
+ end
+
+ def test_add_belongs_to_alias
+ add_belongs_to table_name, :user
+ assert column_exists?(table_name, :user_id, :integer)
+ end
+
+ def test_remove_belongs_to_alias
+ remove_belongs_to table_name, :supplier
+ refute column_exists?(table_name, :supplier_id, :integer)
+ end
+
+ private
+
+ def with_polymorphic_column
+ add_column table_name, :supplier_type, :string
+ add_index table_name, [:supplier_id, :supplier_type]
+
+ yield
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration/rename_column_test.rb b/activerecord/test/cases/migration/rename_column_test.rb
index d1a85ee5e4..df45445ef2 100644
--- a/activerecord/test/cases/migration/rename_column_test.rb
+++ b/activerecord/test/cases/migration/rename_column_test.rb
@@ -33,7 +33,7 @@ module ActiveRecord
rename_column :test_models, :first_name, :nick_name
TestModel.reset_column_information
assert TestModel.column_names.include?("nick_name")
- assert_equal ['foo'], TestModel.all.map(&:nick_name)
+ assert_equal ['foo'], TestModel.to_a.map(&:nick_name)
end
# FIXME: another integration test. We should decouple this from the
@@ -46,7 +46,7 @@ module ActiveRecord
rename_column "test_models", "first_name", "nick_name"
TestModel.reset_column_information
assert TestModel.column_names.include?("nick_name")
- assert_equal ['foo'], TestModel.all.map(&:nick_name)
+ assert_equal ['foo'], TestModel.to_a.map(&:nick_name)
end
def test_rename_column_preserves_default_value_not_null
diff --git a/activerecord/test/cases/migration/rename_table_test.rb b/activerecord/test/cases/migration/rename_table_test.rb
index d5ff2c607f..21901bec3c 100644
--- a/activerecord/test/cases/migration/rename_table_test.rb
+++ b/activerecord/test/cases/migration/rename_table_test.rb
@@ -14,6 +14,11 @@ module ActiveRecord
remove_column 'test_models', :updated_at
end
+ def teardown
+ rename_table :octopi, :test_models if connection.table_exists? :octopi
+ super
+ end
+
def test_rename_table_for_sqlite_should_work_with_reserved_words
renamed = false
@@ -26,8 +31,7 @@ module ActiveRecord
renamed = true
# Using explicit id in insert for compatibility across all databases
- con = connection
- con.execute "INSERT INTO 'references' (url, created_at, updated_at) VALUES ('http://rubyonrails.com', 0, 0)"
+ connection.execute "INSERT INTO 'references' (url, created_at, updated_at) VALUES ('http://rubyonrails.com', 0, 0)"
assert_equal 'http://rubyonrails.com', connection.select_value("SELECT url FROM 'references' WHERE id=1")
ensure
return unless renamed
@@ -39,16 +43,13 @@ module ActiveRecord
rename_table :test_models, :octopi
# Using explicit id in insert for compatibility across all databases
- con = connection
- con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
+ connection.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
- con.execute "INSERT INTO octopi (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"
+ connection.execute "INSERT INTO octopi (#{connection.quote_column_name('id')}, #{connection.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"
- con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
+ connection.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', connection.select_value("SELECT url FROM octopi WHERE id=1")
-
- rename_table :octopi, :test_models
end
def test_rename_table_with_an_index
@@ -57,15 +58,22 @@ module ActiveRecord
rename_table :test_models, :octopi
# Using explicit id in insert for compatibility across all databases
- con = ActiveRecord::Base.connection
- con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
- con.execute "INSERT INTO octopi (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"
- con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
+ connection.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
+ connection.execute "INSERT INTO octopi (#{connection.quote_column_name('id')}, #{connection.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"
+ connection.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', connection.select_value("SELECT url FROM octopi WHERE id=1")
assert connection.indexes(:octopi).first.columns.include?("url")
+ end
+
+ def test_rename_table_for_postgresql_should_also_rename_default_sequence
+ skip 'not supported' unless current_adapter?(:PostgreSQLAdapter)
+
+ rename_table :test_models, :octopi
+
+ pk, seq = connection.pk_and_sequence_for('octopi')
- rename_table :octopi, :test_models
+ assert_equal "octopi_#{pk}_seq", seq
end
end
end
diff --git a/activerecord/test/cases/modules_test.rb b/activerecord/test/cases/modules_test.rb
index a03c4f552e..08b3408665 100644
--- a/activerecord/test/cases/modules_test.rb
+++ b/activerecord/test/cases/modules_test.rb
@@ -39,7 +39,7 @@ class ModulesTest < ActiveRecord::TestCase
end
def test_associations_spanning_cross_modules
- account = MyApplication::Billing::Account.scoped(:order => 'id').first
+ account = MyApplication::Billing::Account.all.merge!(:order => 'id').first
assert_kind_of MyApplication::Business::Firm, account.firm
assert_kind_of MyApplication::Billing::Firm, account.qualified_billing_firm
assert_kind_of MyApplication::Billing::Firm, account.unqualified_billing_firm
@@ -48,7 +48,7 @@ class ModulesTest < ActiveRecord::TestCase
end
def test_find_account_and_include_company
- account = MyApplication::Billing::Account.scoped(:includes => :firm).find(1)
+ account = MyApplication::Billing::Account.all.merge!(:includes => :firm).find(1)
assert_kind_of MyApplication::Business::Firm, account.firm
end
@@ -72,8 +72,8 @@ class ModulesTest < ActiveRecord::TestCase
clients = []
assert_nothing_raised NameError, "Should be able to resolve all class constants via reflection" do
- clients << MyApplication::Business::Client.references(:accounts).scoped(:includes => {:firm => :account}, :where => 'accounts.id IS NOT NULL').find(3)
- clients << MyApplication::Business::Client.scoped(:includes => {:firm => :account}).find(3)
+ clients << MyApplication::Business::Client.references(:accounts).merge!(:includes => {:firm => :account}, :where => 'accounts.id IS NOT NULL').find(3)
+ clients << MyApplication::Business::Client.includes(:firm => :account).find(3)
end
clients.each do |client|
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index c886160af3..9f0e2d5fc8 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -10,12 +10,12 @@ class NamedScopeTest < ActiveRecord::TestCase
fixtures :posts, :authors, :topics, :comments, :author_addresses
def test_implements_enumerable
- assert !Topic.all.empty?
+ assert !Topic.to_a.empty?
- assert_equal Topic.all, Topic.base
- assert_equal Topic.all, Topic.base.to_a
+ assert_equal Topic.to_a, Topic.base
+ assert_equal Topic.to_a, Topic.base.to_a
assert_equal Topic.first, Topic.base.first
- assert_equal Topic.all, Topic.base.map { |i| i }
+ assert_equal Topic.to_a, Topic.base.map { |i| i }
end
def test_found_items_are_cached
@@ -29,7 +29,7 @@ class NamedScopeTest < ActiveRecord::TestCase
def test_reload_expires_cache_of_found_items
all_posts = Topic.base
- all_posts.all
+ all_posts.to_a
new_post = Topic.create!
assert !all_posts.include?(new_post)
@@ -37,9 +37,9 @@ class NamedScopeTest < ActiveRecord::TestCase
end
def test_delegates_finds_and_calculations_to_the_base_class
- assert !Topic.all.empty?
+ assert !Topic.to_a.empty?
- assert_equal Topic.all, Topic.base.all
+ assert_equal Topic.to_a, Topic.base.to_a
assert_equal Topic.first, Topic.base.first
assert_equal Topic.count, Topic.base.count
assert_equal Topic.average(:replies_count), Topic.base.average(:replies_count)
@@ -51,7 +51,7 @@ class NamedScopeTest < ActiveRecord::TestCase
scope :since, Proc.new { where('written_on >= ?', Time.now - 1.day) }
scope :to, Proc.new { where('written_on <= ?', Time.now) }
end
- assert_equal klazz.to.since.all, klazz.since.to.all
+ assert_equal klazz.to.since.to_a, klazz.since.to.to_a
end
def test_scope_should_respond_to_own_methods_and_methods_of_the_proxy
@@ -66,9 +66,9 @@ class NamedScopeTest < ActiveRecord::TestCase
end
def test_scopes_with_options_limit_finds_to_those_matching_the_criteria_specified
- assert !Topic.scoped(:where => {:approved => true}).all.empty?
+ assert !Topic.all.merge!(:where => {:approved => true}).to_a.empty?
- assert_equal Topic.scoped(:where => {:approved => true}).all, Topic.approved
+ assert_equal Topic.all.merge!(:where => {:approved => true}).to_a, Topic.approved
assert_equal Topic.where(:approved => true).count, Topic.approved.count
end
@@ -79,8 +79,8 @@ class NamedScopeTest < ActiveRecord::TestCase
end
def test_scopes_are_composable
- assert_equal((approved = Topic.scoped(:where => {:approved => true}).all), Topic.approved)
- assert_equal((replied = Topic.scoped(:where => 'replies_count > 0').all), Topic.replied)
+ assert_equal((approved = Topic.all.merge!(:where => {:approved => true}).to_a), Topic.approved)
+ assert_equal((replied = Topic.all.merge!(:where => 'replies_count > 0').to_a), Topic.replied)
assert !(approved == replied)
assert !(approved & replied).empty?
@@ -97,7 +97,7 @@ class NamedScopeTest < ActiveRecord::TestCase
end
def test_procedural_scopes_returning_nil
- all_topics = Topic.all
+ all_topics = Topic.to_a
assert_equal all_topics, Topic.written_before(nil)
end
@@ -138,16 +138,16 @@ class NamedScopeTest < ActiveRecord::TestCase
end
def test_active_records_have_scope_named__all__
- assert !Topic.all.empty?
+ assert !Topic.to_a.empty?
- assert_equal Topic.all, Topic.base
+ assert_equal Topic.to_a, Topic.base
end
def test_active_records_have_scope_named__scoped__
scope = Topic.where("content LIKE '%Have%'")
assert !scope.empty?
- assert_equal scope, Topic.scoped(where: "content LIKE '%Have%'")
+ assert_equal scope, Topic.all.merge!(where: "content LIKE '%Have%'")
end
def test_first_and_last_should_allow_integers_for_limit
@@ -325,14 +325,14 @@ class NamedScopeTest < ActiveRecord::TestCase
def test_chaining_should_use_latest_conditions_when_searching
# Normal hash conditions
- assert_equal Topic.where(:approved => true).to_a, Topic.rejected.approved.all
- assert_equal Topic.where(:approved => false).to_a, Topic.approved.rejected.all
+ assert_equal Topic.where(:approved => true).to_a, Topic.rejected.approved.to_a
+ assert_equal Topic.where(:approved => false).to_a, Topic.approved.rejected.to_a
# Nested hash conditions with same keys
- assert_equal [posts(:sti_comments)], Post.with_special_comments.with_very_special_comments.all
+ assert_equal [posts(:sti_comments)], Post.with_special_comments.with_very_special_comments.to_a
# Nested hash conditions with different keys
- assert_equal [posts(:sti_comments)], Post.with_special_comments.with_post(4).all.uniq
+ assert_equal [posts(:sti_comments)], Post.with_special_comments.with_post(4).to_a.uniq
end
def test_scopes_batch_finders
@@ -351,13 +351,13 @@ class NamedScopeTest < ActiveRecord::TestCase
def test_table_names_for_chaining_scopes_with_and_without_table_name_included
assert_nothing_raised do
- Comment.for_first_post.for_first_author.all
+ Comment.for_first_post.for_first_author.to_a
end
end
def test_scopes_on_relations
# Topic.replied
- approved_topics = Topic.scoped.approved.order('id DESC')
+ approved_topics = Topic.all.approved.order('id DESC')
assert_equal topics(:fourth), approved_topics.first
replied_approved_topics = approved_topics.replied
@@ -372,7 +372,7 @@ class NamedScopeTest < ActiveRecord::TestCase
def test_nested_scopes_queries_size
assert_queries(1) do
- Topic.approved.by_lifo.replied.written_before(Time.now).all
+ Topic.approved.by_lifo.replied.written_before(Time.now).to_a
end
end
@@ -383,8 +383,8 @@ class NamedScopeTest < ActiveRecord::TestCase
post = posts(:welcome)
Post.cache do
- assert_queries(1) { post.comments.containing_the_letter_e.all }
- assert_no_queries { post.comments.containing_the_letter_e.all }
+ assert_queries(1) { post.comments.containing_the_letter_e.to_a }
+ assert_no_queries { post.comments.containing_the_letter_e.to_a }
end
end
@@ -392,14 +392,14 @@ class NamedScopeTest < ActiveRecord::TestCase
post = posts(:welcome)
Post.cache do
- one = assert_queries(1) { post.comments.limit_by(1).all }
+ one = assert_queries(1) { post.comments.limit_by(1).to_a }
assert_equal 1, one.size
- two = assert_queries(1) { post.comments.limit_by(2).all }
+ two = assert_queries(1) { post.comments.limit_by(2).to_a }
assert_equal 2, two.size
- assert_no_queries { post.comments.limit_by(1).all }
- assert_no_queries { post.comments.limit_by(2).all }
+ assert_no_queries { post.comments.limit_by(1).to_a }
+ assert_no_queries { post.comments.limit_by(2).to_a }
end
end
@@ -444,6 +444,6 @@ class NamedScopeTest < ActiveRecord::TestCase
assert_deprecated do
klass.send(:default_scope, klass.where(:id => posts(:welcome).id))
end
- assert_equal [posts(:welcome).title], klass.all.map(&:title)
+ assert_equal [posts(:welcome).title], klass.to_a.map(&:title)
end
end
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 3daa033ed0..3a234f0cc1 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -196,7 +196,7 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
end
def test_should_raise_argument_error_if_trying_to_build_polymorphic_belongs_to
- assert_raise_with_message ArgumentError, "Cannot build association looter. Are you trying to build a polymorphic one-to-one association?" do
+ assert_raise_with_message ArgumentError, "Cannot build association `looter'. Are you trying to build a polymorphic one-to-one association?" do
Treasure.new(:name => 'pearl', :looter_attributes => {:catchphrase => "Arrr"})
end
end
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index b7b77b24af..fa65d957b8 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -55,7 +55,7 @@ class PersistencesTest < ActiveRecord::TestCase
author = authors(:david)
assert_nothing_raised do
assert_equal 1, author.posts_sorted_by_id_limited.size
- assert_equal 2, author.posts_sorted_by_id_limited.scoped(:limit => 2).all.size
+ assert_equal 2, author.posts_sorted_by_id_limited.limit(2).to_a.size
assert_equal 1, author.posts_sorted_by_id_limited.update_all([ "body = ?", "bulk update!" ])
assert_equal "bulk update!", posts(:welcome).body
assert_not_equal "bulk update!", posts(:thinking).body
@@ -120,7 +120,7 @@ class PersistencesTest < ActiveRecord::TestCase
def test_destroy_all
conditions = "author_name = 'Mary'"
- topics_by_mary = Topic.scoped(:where => conditions, :order => 'id').to_a
+ topics_by_mary = Topic.all.merge!(:where => conditions, :order => 'id').to_a
assert ! topics_by_mary.empty?
assert_difference('Topic.count', -topics_by_mary.size) do
@@ -131,7 +131,7 @@ class PersistencesTest < ActiveRecord::TestCase
end
def test_destroy_many
- clients = Client.scoped(:order => 'id').find([2, 3])
+ clients = Client.all.merge!(:order => 'id').find([2, 3])
assert_difference('Client.count', -2) do
destroyed = Client.destroy([2, 3]).sort_by(&:id)
@@ -377,22 +377,21 @@ class PersistencesTest < ActiveRecord::TestCase
def test_update_column
topic = Topic.find(1)
- topic.update_column("approved", true)
+ assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do
+ topic.update_column("approved", true)
+ end
assert topic.approved?
topic.reload
assert topic.approved?
-
- topic.update_column(:approved, false)
- assert !topic.approved?
- topic.reload
- assert !topic.approved?
end
def test_update_column_should_not_use_setter_method
dev = Developer.find(1)
dev.instance_eval { def salary=(value); write_attribute(:salary, value * 2); end }
- dev.update_column(:salary, 80000)
+ assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do
+ dev.update_column(:salary, 80000)
+ end
assert_equal 80000, dev.salary
dev.reload
@@ -401,19 +400,29 @@ class PersistencesTest < ActiveRecord::TestCase
def test_update_column_should_raise_exception_if_new_record
topic = Topic.new
- assert_raises(ActiveRecord::ActiveRecordError) { topic.update_column("approved", false) }
+ assert_raises(ActiveRecord::ActiveRecordError) do
+ assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do
+ topic.update_column("approved", false)
+ end
+ end
end
def test_update_column_should_not_leave_the_object_dirty
topic = Topic.find(1)
- topic.update_column("content", "Have a nice day")
+ assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do
+ topic.update_column("content", "Have a nice day")
+ end
topic.reload
- topic.update_column(:content, "You too")
+ assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do
+ topic.update_column(:content, "You too")
+ end
assert_equal [], topic.changed
topic.reload
- topic.update_column("content", "Have a nice day")
+ assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do
+ topic.update_column("content", "Have a nice day")
+ end
assert_equal [], topic.changed
end
@@ -421,14 +430,20 @@ class PersistencesTest < ActiveRecord::TestCase
minivan = Minivan.find('m1')
new_name = 'sebavan'
- minivan.update_column(:name, new_name)
+ assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do
+ minivan.update_column(:name, new_name)
+ end
assert_equal new_name, minivan.name
end
def test_update_column_for_readonly_attribute
minivan = Minivan.find('m1')
prev_color = minivan.color
- assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_column(:color, 'black') }
+ assert_raises(ActiveRecord::ActiveRecordError) do
+ assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do
+ minivan.update_column(:color, 'black')
+ end
+ end
assert_equal prev_color, minivan.color
end
@@ -436,10 +451,14 @@ class PersistencesTest < ActiveRecord::TestCase
developer = Developer.find(1)
prev_month = Time.now.prev_month
- developer.update_column(:updated_at, prev_month)
+ assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do
+ developer.update_column(:updated_at, prev_month)
+ end
assert_equal prev_month, developer.updated_at
- developer.update_column(:salary, 80001)
+ assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do
+ developer.update_column(:salary, 80001)
+ end
assert_equal prev_month, developer.updated_at
developer.reload
@@ -448,9 +467,102 @@ class PersistencesTest < ActiveRecord::TestCase
def test_update_column_with_one_changed_and_one_updated
t = Topic.order('id').limit(1).first
- title, author_name = t.title, t.author_name
+ author_name = t.author_name
+ t.author_name = 'John'
+ assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do
+ t.update_column(:title, 'super_title')
+ end
+ assert_equal 'John', t.author_name
+ assert_equal 'super_title', t.title
+ assert t.changed?, "topic should have changed"
+ assert t.author_name_changed?, "author_name should have changed"
+
+ t.reload
+ assert_equal author_name, t.author_name
+ assert_equal 'super_title', t.title
+ end
+
+ def test_update_columns
+ topic = Topic.find(1)
+ topic.update_columns({ "approved" => true, title: "Sebastian Topic" })
+ assert topic.approved?
+ assert_equal "Sebastian Topic", topic.title
+ topic.reload
+ assert topic.approved?
+ assert_equal "Sebastian Topic", topic.title
+ end
+
+ def test_update_columns_should_not_use_setter_method
+ dev = Developer.find(1)
+ dev.instance_eval { def salary=(value); write_attribute(:salary, value * 2); end }
+
+ dev.update_columns(salary: 80000)
+ assert_equal 80000, dev.salary
+
+ dev.reload
+ assert_equal 80000, dev.salary
+ end
+
+ def test_update_columns_should_raise_exception_if_new_record
+ topic = Topic.new
+ assert_raises(ActiveRecord::ActiveRecordError) { topic.update_columns({ approved: false }) }
+ end
+
+ def test_update_columns_should_not_leave_the_object_dirty
+ topic = Topic.find(1)
+ topic.update_attributes({ "content" => "Have a nice day", :author_name => "Jose" })
+
+ topic.reload
+ topic.update_columns({ content: "You too", "author_name" => "Sebastian" })
+ assert_equal [], topic.changed
+
+ topic.reload
+ topic.update_columns({ content: "Have a nice day", author_name: "Jose" })
+ assert_equal [], topic.changed
+ end
+
+ def test_update_columns_with_model_having_primary_key_other_than_id
+ minivan = Minivan.find('m1')
+ new_name = 'sebavan'
+
+ minivan.update_columns(name: new_name)
+ assert_equal new_name, minivan.name
+ end
+
+ def test_update_columns_with_one_readonly_attribute
+ minivan = Minivan.find('m1')
+ prev_color = minivan.color
+ prev_name = minivan.name
+ assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_columns({ name: "My old minivan", color: 'black' }) }
+ assert_equal prev_color, minivan.color
+ assert_equal prev_name, minivan.name
+
+ minivan.reload
+ assert_equal prev_color, minivan.color
+ assert_equal prev_name, minivan.name
+ end
+
+ def test_update_columns_should_not_modify_updated_at
+ developer = Developer.find(1)
+ prev_month = Time.now.prev_month
+
+ developer.update_columns(updated_at: prev_month)
+ assert_equal prev_month, developer.updated_at
+
+ developer.update_columns(salary: 80000)
+ assert_equal prev_month, developer.updated_at
+ assert_equal 80000, developer.salary
+
+ developer.reload
+ assert_equal prev_month.to_i, developer.updated_at.to_i
+ assert_equal 80000, developer.salary
+ end
+
+ def test_update_columns_with_one_changed_and_one_updated
+ t = Topic.order('id').limit(1).first
+ author_name = t.author_name
t.author_name = 'John'
- t.update_column(:title, 'super_title')
+ t.update_columns(title: 'super_title')
assert_equal 'John', t.author_name
assert_equal 'super_title', t.title
assert t.changed?, "topic should have changed"
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index 08f655d7fa..2d778e9e90 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -39,6 +39,18 @@ class QueryCacheTest < ActiveRecord::TestCase
assert ActiveRecord::Base.connection.query_cache_enabled, 'cache on'
end
+ def test_exceptional_middleware_assigns_original_connection_id_on_error
+ connection_id = ActiveRecord::Base.connection_id
+
+ mw = ActiveRecord::QueryCache.new lambda { |env|
+ ActiveRecord::Base.connection_id = self.object_id
+ raise "lol borked"
+ }
+ assert_raises(RuntimeError) { mw.call({}) }
+
+ assert_equal connection_id, ActiveRecord::Base.connection_id
+ end
+
def test_middleware_delegates
called = false
mw = ActiveRecord::QueryCache.new lambda { |env|
@@ -155,7 +167,7 @@ class QueryCacheTest < ActiveRecord::TestCase
# Oracle adapter returns count() as Fixnum or Float
if current_adapter?(:OracleAdapter)
assert_kind_of Numeric, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
- elsif current_adapter?(:SQLite3Adapter) || current_adapter?(:Mysql2Adapter) || current_adapter?(:MysqlAdapter)
+ elsif current_adapter?(:SQLite3Adapter) || current_adapter?(:Mysql2Adapter)
# Future versions of the sqlite3 adapter will return numeric
assert_instance_of Fixnum,
Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
@@ -164,6 +176,14 @@ class QueryCacheTest < ActiveRecord::TestCase
end
end
end
+
+ def test_cache_is_ignored_for_locked_relations
+ task = Task.find 1
+
+ Task.cache do
+ assert_queries(2) { task.lock!; task.lock! }
+ end
+ end
end
class QueryCacheExpiryTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb
index 80ee74e41e..3dd11ae89d 100644
--- a/activerecord/test/cases/quoting_test.rb
+++ b/activerecord/test/cases/quoting_test.rb
@@ -216,6 +216,14 @@ module ActiveRecord
def test_string_with_crazy_column
assert_equal "'lo\\\\l'", @quoter.quote('lo\l', FakeColumn.new(:foo))
end
+
+ def test_quote_duration
+ assert_equal "1800", @quoter.quote(30.minutes)
+ end
+
+ def test_quote_duration_int_column
+ assert_equal "7200", @quoter.quote(2.hours, FakeColumn.new(:integer))
+ end
end
end
end
diff --git a/activerecord/test/cases/readonly_test.rb b/activerecord/test/cases/readonly_test.rb
index df0399f548..9b9f11c7de 100644
--- a/activerecord/test/cases/readonly_test.rb
+++ b/activerecord/test/cases/readonly_test.rb
@@ -28,7 +28,7 @@ class ReadOnlyTest < ActiveRecord::TestCase
def test_find_with_readonly_option
- Developer.all.each { |d| assert !d.readonly? }
+ Developer.to_a.each { |d| assert !d.readonly? }
Developer.readonly(false).each { |d| assert !d.readonly? }
Developer.readonly(true).each { |d| assert d.readonly? }
Developer.readonly.each { |d| assert d.readonly? }
@@ -49,7 +49,7 @@ class ReadOnlyTest < ActiveRecord::TestCase
post = Post.find(1)
assert !post.comments.empty?
assert !post.comments.any?(&:readonly?)
- assert !post.comments.all.any?(&:readonly?)
+ assert !post.comments.to_a.any?(&:readonly?)
assert post.comments.readonly(true).all?(&:readonly?)
end
@@ -71,13 +71,13 @@ class ReadOnlyTest < ActiveRecord::TestCase
end
def test_readonly_scoping
- Post.where('1=1').scoped do
+ Post.where('1=1').scoping do
assert !Post.find(1).readonly?
assert Post.readonly(true).find(1).readonly?
assert !Post.readonly(false).find(1).readonly?
end
- Post.joins(' ').scoped do
+ Post.joins(' ').scoping do
assert !Post.find(1).readonly?
assert Post.readonly.find(1).readonly?
assert !Post.readonly(false).find(1).readonly?
@@ -86,14 +86,14 @@ class ReadOnlyTest < ActiveRecord::TestCase
# Oracle barfs on this because the join includes unqualified and
# conflicting column names
unless current_adapter?(:OracleAdapter)
- Post.joins(', developers').scoped do
+ Post.joins(', developers').scoping do
assert Post.find(1).readonly?
assert Post.readonly.find(1).readonly?
assert !Post.readonly(false).find(1).readonly?
end
end
- Post.readonly(true).scoped do
+ Post.readonly(true).scoping do
assert Post.find(1).readonly?
assert Post.readonly.find(1).readonly?
assert !Post.readonly(false).find(1).readonly?
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index 6631dce08f..51f07b6a2f 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -77,7 +77,7 @@ class ReflectionTest < ActiveRecord::TestCase
end
def test_reflection_klass_for_nested_class_name
- reflection = MacroReflection.new(:company, nil, { :class_name => 'MyApplication::Business::Company' }, ActiveRecord::Base)
+ reflection = MacroReflection.new(:company, nil, nil, { :class_name => 'MyApplication::Business::Company' }, ActiveRecord::Base)
assert_nothing_raised do
assert_equal MyApplication::Business::Company, reflection.klass
end
@@ -93,7 +93,7 @@ class ReflectionTest < ActiveRecord::TestCase
end
def test_has_many_reflection
- reflection_for_clients = AssociationReflection.new(:has_many, :clients, { :order => "id", :dependent => :destroy }, Firm)
+ reflection_for_clients = AssociationReflection.new(:has_many, :clients, nil, { :order => "id", :dependent => :destroy }, Firm)
assert_equal reflection_for_clients, Firm.reflect_on_association(:clients)
@@ -105,7 +105,7 @@ class ReflectionTest < ActiveRecord::TestCase
end
def test_has_one_reflection
- reflection_for_account = AssociationReflection.new(:has_one, :account, { :foreign_key => "firm_id", :dependent => :destroy }, Firm)
+ reflection_for_account = AssociationReflection.new(:has_one, :account, nil, { :foreign_key => "firm_id", :dependent => :destroy }, Firm)
assert_equal reflection_for_account, Firm.reflect_on_association(:account)
assert_equal Account, Firm.reflect_on_association(:account).klass
@@ -165,8 +165,8 @@ class ReflectionTest < ActiveRecord::TestCase
def test_reflection_of_all_associations
# FIXME these assertions bust a lot
- assert_equal 39, Firm.reflect_on_all_associations.size
- assert_equal 29, Firm.reflect_on_all_associations(:has_many).size
+ assert_equal 34, Firm.reflect_on_all_associations.size
+ assert_equal 24, Firm.reflect_on_all_associations(:has_many).size
assert_equal 10, Firm.reflect_on_all_associations(:has_one).size
assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size
end
@@ -190,21 +190,25 @@ class ReflectionTest < ActiveRecord::TestCase
assert_equal expected, actual
end
- def test_conditions
+ def test_scope_chain
expected = [
- [{ :tags => { :name => 'Blue' } }],
- [{ :taggings => { :comment => 'first' } }],
- [{ :posts => { :title => ['misc post by bob', 'misc post by mary'] } }]
+ [Tagging.reflect_on_association(:tag).scope, Post.reflect_on_association(:first_blue_tags).scope],
+ [Post.reflect_on_association(:first_taggings).scope],
+ [Author.reflect_on_association(:misc_posts).scope]
]
- actual = Author.reflect_on_association(:misc_post_first_blue_tags).conditions
+ actual = Author.reflect_on_association(:misc_post_first_blue_tags).scope_chain
assert_equal expected, actual
expected = [
- [{ :tags => { :name => 'Blue' } }, { :taggings => { :comment => 'first' } }, { :posts => { :title => ['misc post by bob', 'misc post by mary'] } }],
+ [
+ Tagging.reflect_on_association(:blue_tag).scope,
+ Post.reflect_on_association(:first_blue_tags_2).scope,
+ Author.reflect_on_association(:misc_post_first_blue_tags_2).scope
+ ],
[],
[]
]
- actual = Author.reflect_on_association(:misc_post_first_blue_tags_2).conditions
+ actual = Author.reflect_on_association(:misc_post_first_blue_tags_2).scope_chain
assert_equal expected, actual
end
@@ -230,10 +234,10 @@ class ReflectionTest < ActiveRecord::TestCase
end
def test_association_primary_key_raises_when_missing_primary_key
- reflection = ActiveRecord::Reflection::AssociationReflection.new(:fuu, :edge, {}, Author)
+ reflection = ActiveRecord::Reflection::AssociationReflection.new(:fuu, :edge, nil, {}, Author)
assert_raises(ActiveRecord::UnknownPrimaryKey) { reflection.association_primary_key }
- through = ActiveRecord::Reflection::ThroughReflection.new(:fuu, :edge, {}, Author)
+ through = ActiveRecord::Reflection::ThroughReflection.new(:fuu, :edge, nil, {}, Author)
through.stubs(:source_reflection).returns(stub_everything(:options => {}, :class_name => 'Edge'))
assert_raises(ActiveRecord::UnknownPrimaryKey) { through.association_primary_key }
end
@@ -244,7 +248,7 @@ class ReflectionTest < ActiveRecord::TestCase
end
def test_active_record_primary_key_raises_when_missing_primary_key
- reflection = ActiveRecord::Reflection::AssociationReflection.new(:fuu, :author, {}, Edge)
+ reflection = ActiveRecord::Reflection::AssociationReflection.new(:fuu, :author, nil, {}, Edge)
assert_raises(ActiveRecord::UnknownPrimaryKey) { reflection.active_record_primary_key }
end
@@ -262,32 +266,32 @@ class ReflectionTest < ActiveRecord::TestCase
end
def test_default_association_validation
- assert AssociationReflection.new(:has_many, :clients, {}, Firm).validate?
+ assert AssociationReflection.new(:has_many, :clients, nil, {}, Firm).validate?
- assert !AssociationReflection.new(:has_one, :client, {}, Firm).validate?
- assert !AssociationReflection.new(:belongs_to, :client, {}, Firm).validate?
- assert !AssociationReflection.new(:has_and_belongs_to_many, :clients, {}, Firm).validate?
+ assert !AssociationReflection.new(:has_one, :client, nil, {}, Firm).validate?
+ assert !AssociationReflection.new(:belongs_to, :client, nil, {}, Firm).validate?
+ assert !AssociationReflection.new(:has_and_belongs_to_many, :clients, nil, {}, Firm).validate?
end
def test_always_validate_association_if_explicit
- assert AssociationReflection.new(:has_one, :client, { :validate => true }, Firm).validate?
- assert AssociationReflection.new(:belongs_to, :client, { :validate => true }, Firm).validate?
- assert AssociationReflection.new(:has_many, :clients, { :validate => true }, Firm).validate?
- assert AssociationReflection.new(:has_and_belongs_to_many, :clients, { :validate => true }, Firm).validate?
+ assert AssociationReflection.new(:has_one, :client, nil, { :validate => true }, Firm).validate?
+ assert AssociationReflection.new(:belongs_to, :client, nil, { :validate => true }, Firm).validate?
+ assert AssociationReflection.new(:has_many, :clients, nil, { :validate => true }, Firm).validate?
+ assert AssociationReflection.new(:has_and_belongs_to_many, :clients, nil, { :validate => true }, Firm).validate?
end
def test_validate_association_if_autosave
- assert AssociationReflection.new(:has_one, :client, { :autosave => true }, Firm).validate?
- assert AssociationReflection.new(:belongs_to, :client, { :autosave => true }, Firm).validate?
- assert AssociationReflection.new(:has_many, :clients, { :autosave => true }, Firm).validate?
- assert AssociationReflection.new(:has_and_belongs_to_many, :clients, { :autosave => true }, Firm).validate?
+ assert AssociationReflection.new(:has_one, :client, nil, { :autosave => true }, Firm).validate?
+ assert AssociationReflection.new(:belongs_to, :client, nil, { :autosave => true }, Firm).validate?
+ assert AssociationReflection.new(:has_many, :clients, nil, { :autosave => true }, Firm).validate?
+ assert AssociationReflection.new(:has_and_belongs_to_many, :clients, nil, { :autosave => true }, Firm).validate?
end
def test_never_validate_association_if_explicit
- assert !AssociationReflection.new(:has_one, :client, { :autosave => true, :validate => false }, Firm).validate?
- assert !AssociationReflection.new(:belongs_to, :client, { :autosave => true, :validate => false }, Firm).validate?
- assert !AssociationReflection.new(:has_many, :clients, { :autosave => true, :validate => false }, Firm).validate?
- assert !AssociationReflection.new(:has_and_belongs_to_many, :clients, { :autosave => true, :validate => false }, Firm).validate?
+ assert !AssociationReflection.new(:has_one, :client, nil, { :autosave => true, :validate => false }, Firm).validate?
+ assert !AssociationReflection.new(:belongs_to, :client, nil, { :autosave => true, :validate => false }, Firm).validate?
+ assert !AssociationReflection.new(:has_many, :clients, nil, { :autosave => true, :validate => false }, Firm).validate?
+ assert !AssociationReflection.new(:has_and_belongs_to_many, :clients, nil, { :autosave => true, :validate => false }, Firm).validate?
end
def test_foreign_key
@@ -295,10 +299,10 @@ class ReflectionTest < ActiveRecord::TestCase
assert_equal "category_id", Post.reflect_on_association(:categorizations).foreign_key.to_s
end
- def test_through_reflection_conditions_do_not_modify_other_reflections
- orig_conds = Post.reflect_on_association(:first_blue_tags_2).conditions.inspect
- Author.reflect_on_association(:misc_post_first_blue_tags_2).conditions
- assert_equal orig_conds, Post.reflect_on_association(:first_blue_tags_2).conditions.inspect
+ def test_through_reflection_scope_chain_does_not_modify_other_reflections
+ orig_conds = Post.reflect_on_association(:first_blue_tags_2).scope_chain.inspect
+ Author.reflect_on_association(:misc_post_first_blue_tags_2).scope_chain
+ assert_equal orig_conds, Post.reflect_on_association(:first_blue_tags_2).scope_chain.inspect
end
def test_symbol_for_class_name
@@ -309,11 +313,11 @@ class ReflectionTest < ActiveRecord::TestCase
category = Struct.new(:table_name, :pluralize_table_names).new('categories', true)
product = Struct.new(:table_name, :pluralize_table_names).new('products', true)
- reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, {}, product)
+ reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, nil, {}, product)
reflection.stubs(:klass).returns(category)
assert_equal 'categories_products', reflection.join_table
- reflection = AssociationReflection.new(:has_and_belongs_to_many, :products, {}, category)
+ reflection = AssociationReflection.new(:has_and_belongs_to_many, :products, nil, {}, category)
reflection.stubs(:klass).returns(product)
assert_equal 'categories_products', reflection.join_table
end
@@ -322,11 +326,11 @@ class ReflectionTest < ActiveRecord::TestCase
category = Struct.new(:table_name, :pluralize_table_names).new('catalog_categories', true)
product = Struct.new(:table_name, :pluralize_table_names).new('catalog_products', true)
- reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, {}, product)
+ reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, nil, {}, product)
reflection.stubs(:klass).returns(category)
assert_equal 'catalog_categories_products', reflection.join_table
- reflection = AssociationReflection.new(:has_and_belongs_to_many, :products, {}, category)
+ reflection = AssociationReflection.new(:has_and_belongs_to_many, :products, nil, {}, category)
reflection.stubs(:klass).returns(product)
assert_equal 'catalog_categories_products', reflection.join_table
end
@@ -335,11 +339,11 @@ class ReflectionTest < ActiveRecord::TestCase
category = Struct.new(:table_name, :pluralize_table_names).new('catalog_categories', true)
page = Struct.new(:table_name, :pluralize_table_names).new('content_pages', true)
- reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, {}, page)
+ reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, nil, {}, page)
reflection.stubs(:klass).returns(category)
assert_equal 'catalog_categories_content_pages', reflection.join_table
- reflection = AssociationReflection.new(:has_and_belongs_to_many, :pages, {}, category)
+ reflection = AssociationReflection.new(:has_and_belongs_to_many, :pages, nil, {}, category)
reflection.stubs(:klass).returns(page)
assert_equal 'catalog_categories_content_pages', reflection.join_table
end
@@ -348,11 +352,11 @@ class ReflectionTest < ActiveRecord::TestCase
category = Struct.new(:table_name, :pluralize_table_names).new('categories', true)
product = Struct.new(:table_name, :pluralize_table_names).new('products', true)
- reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, { :join_table => 'product_categories' }, product)
+ reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, nil, { :join_table => 'product_categories' }, product)
reflection.stubs(:klass).returns(category)
assert_equal 'product_categories', reflection.join_table
- reflection = AssociationReflection.new(:has_and_belongs_to_many, :products, { :join_table => 'product_categories' }, category)
+ reflection = AssociationReflection.new(:has_and_belongs_to_many, :products, nil, { :join_table => 'product_categories' }, category)
reflection.stubs(:klass).returns(product)
assert_equal 'product_categories', reflection.join_table
end
diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb
index cf367242f2..ba77bdcc8b 100644
--- a/activerecord/test/cases/relation_scoping_test.rb
+++ b/activerecord/test/cases/relation_scoping_test.rb
@@ -70,7 +70,7 @@ class RelationScopingTest < ActiveRecord::TestCase
def test_scoped_find_all
Developer.where("name = 'David'").scoping do
- assert_equal [developers(:david)], Developer.all
+ assert_equal [developers(:david)], Developer.to_a
end
end
@@ -106,7 +106,7 @@ class RelationScopingTest < ActiveRecord::TestCase
def test_scoped_find_include
# with the include, will retrieve only developers for the given project
scoped_developers = Developer.includes(:projects).scoping do
- Developer.where('projects.id' => 2).all
+ Developer.where('projects.id' => 2).to_a
end
assert scoped_developers.include?(developers(:david))
assert !scoped_developers.include?(developers(:jamis))
@@ -115,7 +115,7 @@ class RelationScopingTest < ActiveRecord::TestCase
def test_scoped_find_joins
scoped_developers = Developer.joins('JOIN developers_projects ON id = developer_id').scoping do
- Developer.where('developers_projects.project_id = 2').all
+ Developer.where('developers_projects.project_id = 2').to_a
end
assert scoped_developers.include?(developers(:david))
@@ -159,7 +159,7 @@ class RelationScopingTest < ActiveRecord::TestCase
rescue
end
- assert !Developer.scoped.where_values.include?("name = 'Jamis'")
+ assert !Developer.all.where_values.include?("name = 'Jamis'")
end
end
@@ -169,7 +169,7 @@ class NestedRelationScopingTest < ActiveRecord::TestCase
def test_merge_options
Developer.where('salary = 80000').scoping do
Developer.limit(10).scoping do
- devs = Developer.scoped
+ devs = Developer.all
assert_match '(salary = 80000)', devs.to_sql
assert_equal 10, devs.taken
end
@@ -179,7 +179,7 @@ class NestedRelationScopingTest < ActiveRecord::TestCase
def test_merge_inner_scope_has_priority
Developer.limit(5).scoping do
Developer.limit(10).scoping do
- assert_equal 10, Developer.all.size
+ assert_equal 10, Developer.to_a.size
end
end
end
@@ -312,33 +312,33 @@ class DefaultScopingTest < ActiveRecord::TestCase
fixtures :developers, :posts
def test_default_scope
- expected = Developer.scoped(:order => 'salary DESC').all.collect { |dev| dev.salary }
- received = DeveloperOrderedBySalary.all.collect { |dev| dev.salary }
+ expected = Developer.all.merge!(:order => 'salary DESC').to_a.collect { |dev| dev.salary }
+ received = DeveloperOrderedBySalary.to_a.collect { |dev| dev.salary }
assert_equal expected, received
end
def test_default_scope_as_class_method
- assert_equal [developers(:david).becomes(ClassMethodDeveloperCalledDavid)], ClassMethodDeveloperCalledDavid.all
+ assert_equal [developers(:david).becomes(ClassMethodDeveloperCalledDavid)], ClassMethodDeveloperCalledDavid.to_a
end
def test_default_scope_as_class_method_referencing_scope
- assert_equal [developers(:david).becomes(ClassMethodReferencingScopeDeveloperCalledDavid)], ClassMethodReferencingScopeDeveloperCalledDavid.all
+ assert_equal [developers(:david).becomes(ClassMethodReferencingScopeDeveloperCalledDavid)], ClassMethodReferencingScopeDeveloperCalledDavid.to_a
end
def test_default_scope_as_block_referencing_scope
- assert_equal [developers(:david).becomes(LazyBlockReferencingScopeDeveloperCalledDavid)], LazyBlockReferencingScopeDeveloperCalledDavid.all
+ assert_equal [developers(:david).becomes(LazyBlockReferencingScopeDeveloperCalledDavid)], LazyBlockReferencingScopeDeveloperCalledDavid.to_a
end
def test_default_scope_with_lambda
- assert_equal [developers(:david).becomes(LazyLambdaDeveloperCalledDavid)], LazyLambdaDeveloperCalledDavid.all
+ assert_equal [developers(:david).becomes(LazyLambdaDeveloperCalledDavid)], LazyLambdaDeveloperCalledDavid.to_a
end
def test_default_scope_with_block
- assert_equal [developers(:david).becomes(LazyBlockDeveloperCalledDavid)], LazyBlockDeveloperCalledDavid.all
+ assert_equal [developers(:david).becomes(LazyBlockDeveloperCalledDavid)], LazyBlockDeveloperCalledDavid.to_a
end
def test_default_scope_with_callable
- assert_equal [developers(:david).becomes(CallableDeveloperCalledDavid)], CallableDeveloperCalledDavid.all
+ assert_equal [developers(:david).becomes(CallableDeveloperCalledDavid)], CallableDeveloperCalledDavid.to_a
end
def test_default_scope_is_unscoped_on_find
@@ -351,42 +351,42 @@ class DefaultScopingTest < ActiveRecord::TestCase
end
def test_default_scope_with_conditions_string
- assert_equal Developer.where(name: 'David').map(&:id).sort, DeveloperCalledDavid.all.map(&:id).sort
+ assert_equal Developer.where(name: 'David').map(&:id).sort, DeveloperCalledDavid.to_a.map(&:id).sort
assert_equal nil, DeveloperCalledDavid.create!.name
end
def test_default_scope_with_conditions_hash
- assert_equal Developer.where(name: 'Jamis').map(&:id).sort, DeveloperCalledJamis.all.map(&:id).sort
+ assert_equal Developer.where(name: 'Jamis').map(&:id).sort, DeveloperCalledJamis.to_a.map(&:id).sort
assert_equal 'Jamis', DeveloperCalledJamis.create!.name
end
def test_default_scoping_with_threads
2.times do
- Thread.new { assert DeveloperOrderedBySalary.scoped.to_sql.include?('salary DESC') }.join
+ Thread.new { assert DeveloperOrderedBySalary.all.to_sql.include?('salary DESC') }.join
end
end
def test_default_scope_with_inheritance
- wheres = InheritedPoorDeveloperCalledJamis.scoped.where_values_hash
+ wheres = InheritedPoorDeveloperCalledJamis.all.where_values_hash
assert_equal "Jamis", wheres[:name]
assert_equal 50000, wheres[:salary]
end
def test_default_scope_with_module_includes
- wheres = ModuleIncludedPoorDeveloperCalledJamis.scoped.where_values_hash
+ wheres = ModuleIncludedPoorDeveloperCalledJamis.all.where_values_hash
assert_equal "Jamis", wheres[:name]
assert_equal 50000, wheres[:salary]
end
def test_default_scope_with_multiple_calls
- wheres = MultiplePoorDeveloperCalledJamis.scoped.where_values_hash
+ wheres = MultiplePoorDeveloperCalledJamis.all.where_values_hash
assert_equal "Jamis", wheres[:name]
assert_equal 50000, wheres[:salary]
end
def test_scope_overwrites_default
- expected = Developer.scoped(:order => 'salary DESC, name DESC').all.collect { |dev| dev.name }
- received = DeveloperOrderedBySalary.by_name.all.collect { |dev| dev.name }
+ expected = Developer.all.merge!(:order => 'salary DESC, name DESC').to_a.collect { |dev| dev.name }
+ received = DeveloperOrderedBySalary.by_name.to_a.collect { |dev| dev.name }
assert_equal expected, received
end
@@ -403,8 +403,8 @@ class DefaultScopingTest < ActiveRecord::TestCase
end
def test_order_in_default_scope_should_prevail
- expected = Developer.scoped(:order => 'salary desc').all.collect { |dev| dev.salary }
- received = DeveloperOrderedBySalary.scoped(:order => 'salary').all.collect { |dev| dev.salary }
+ expected = Developer.all.merge!(:order => 'salary desc').to_a.collect { |dev| dev.salary }
+ received = DeveloperOrderedBySalary.all.merge!(:order => 'salary').to_a.collect { |dev| dev.salary }
assert_equal expected, received
end
@@ -472,16 +472,16 @@ class DefaultScopingTest < ActiveRecord::TestCase
end
def test_default_scope_select_ignored_by_aggregations
- assert_equal DeveloperWithSelect.all.count, DeveloperWithSelect.count
+ assert_equal DeveloperWithSelect.to_a.count, DeveloperWithSelect.count
end
def test_default_scope_select_ignored_by_grouped_aggregations
- assert_equal Hash[Developer.all.group_by(&:salary).map { |s, d| [s, d.count] }],
+ assert_equal Hash[Developer.to_a.group_by(&:salary).map { |s, d| [s, d.count] }],
DeveloperWithSelect.group(:salary).count
end
def test_default_scope_order_ignored_by_aggregations
- assert_equal DeveloperOrderedBySalary.all.count, DeveloperOrderedBySalary.count
+ assert_equal DeveloperOrderedBySalary.to_a.count, DeveloperOrderedBySalary.count
end
def test_default_scope_find_last
@@ -508,10 +508,10 @@ class DefaultScopingTest < ActiveRecord::TestCase
threads << Thread.new do
Thread.current[:long_default_scope] = true
- assert_equal 1, ThreadsafeDeveloper.all.count
+ assert_equal 1, ThreadsafeDeveloper.to_a.count
end
threads << Thread.new do
- assert_equal 1, ThreadsafeDeveloper.all.count
+ assert_equal 1, ThreadsafeDeveloper.to_a.count
end
threads.each(&:join)
end
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index 89f818a689..034339e413 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -191,11 +191,14 @@ module ActiveRecord
end
test 'extending!' do
- mod = Module.new
+ mod, mod2 = Module.new, Module.new
assert relation.extending!(mod).equal?(relation)
- assert [mod], relation.extending_values
+ assert_equal [mod], relation.extending_values
assert relation.is_a?(mod)
+
+ relation.extending!(mod2)
+ assert_equal [mod, mod2], relation.extending_values
end
test 'extending! with empty args' do
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 8544d36aa8..9c64cb35e4 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -34,7 +34,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_bind_values
- relation = Post.scoped
+ relation = Post.all
assert_equal [], relation.bind_values
relation2 = relation.bind 'foo'
@@ -59,44 +59,44 @@ class RelationTest < ActiveRecord::TestCase
end
def test_scoped
- topics = Topic.scoped
+ topics = Topic.all
assert_kind_of ActiveRecord::Relation, topics
assert_equal 4, topics.size
end
def test_to_json
- assert_nothing_raised { Bird.scoped.to_json }
- assert_nothing_raised { Bird.scoped.all.to_json }
+ assert_nothing_raised { Bird.all.to_json }
+ assert_nothing_raised { Bird.all.to_a.to_json }
end
def test_to_yaml
- assert_nothing_raised { Bird.scoped.to_yaml }
- assert_nothing_raised { Bird.scoped.all.to_yaml }
+ assert_nothing_raised { Bird.all.to_yaml }
+ assert_nothing_raised { Bird.all.to_a.to_yaml }
end
def test_to_xml
- assert_nothing_raised { Bird.scoped.to_xml }
- assert_nothing_raised { Bird.scoped.all.to_xml }
+ assert_nothing_raised { Bird.all.to_xml }
+ assert_nothing_raised { Bird.all.to_a.to_xml }
end
def test_scoped_all
- topics = Topic.scoped.all
+ topics = Topic.all.to_a
assert_kind_of Array, topics
assert_no_queries { assert_equal 4, topics.size }
end
def test_loaded_all
- topics = Topic.scoped
+ topics = Topic.all
assert_queries(1) do
- 2.times { assert_equal 4, topics.all.size }
+ 2.times { assert_equal 4, topics.to_a.size }
end
assert topics.loaded?
end
def test_scoped_first
- topics = Topic.scoped.order('id ASC')
+ topics = Topic.all.order('id ASC')
assert_queries(1) do
2.times { assert_equal "The First Topic", topics.first.title }
@@ -106,10 +106,10 @@ class RelationTest < ActiveRecord::TestCase
end
def test_loaded_first
- topics = Topic.scoped.order('id ASC')
+ topics = Topic.all.order('id ASC')
assert_queries(1) do
- topics.all # force load
+ topics.to_a # force load
2.times { assert_equal "The First Topic", topics.first.title }
end
@@ -117,7 +117,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_reload
- topics = Topic.scoped
+ topics = Topic.all
assert_queries(1) do
2.times { topics.to_a }
@@ -171,7 +171,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_finding_with_reorder
- topics = Topic.order('author_name').order('title').reorder('id').all
+ topics = Topic.order('author_name').order('title').reorder('id').to_a
topics_titles = topics.map{ |t| t.title }
assert_equal ['The First Topic', 'The Second Topic of the day', 'The Third Topic of the day', 'The Fourth Topic of the day'], topics_titles
end
@@ -218,14 +218,14 @@ class RelationTest < ActiveRecord::TestCase
end
def test_select_with_block
- even_ids = Developer.scoped.select {|d| d.id % 2 == 0 }.map(&:id)
+ even_ids = Developer.all.select {|d| d.id % 2 == 0 }.map(&:id)
assert_equal [2, 4, 6, 8, 10], even_ids.sort
end
def test_none
assert_no_queries do
assert_equal [], Developer.none
- assert_equal [], Developer.scoped.none
+ assert_equal [], Developer.all.none
end
end
@@ -294,7 +294,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_find_on_hash_conditions
- assert_equal Topic.scoped(:where => {:approved => false}).all, Topic.where({ :approved => false }).to_a
+ assert_equal Topic.all.merge!(:where => {:approved => false}).to_a, Topic.where({ :approved => false }).to_a
end
def test_joins_with_string_array
@@ -307,15 +307,15 @@ class RelationTest < ActiveRecord::TestCase
end
def test_scoped_responds_to_delegated_methods
- relation = Topic.scoped
+ relation = Topic.all
["map", "uniq", "sort", "insert", "delete", "update"].each do |method|
- assert_respond_to relation, method, "Topic.scoped should respond to #{method.inspect}"
+ assert_respond_to relation, method, "Topic.all should respond to #{method.inspect}"
end
end
def test_respond_to_delegates_to_relation
- relation = Topic.scoped
+ relation = Topic.all
fake_arel = Struct.new(:responds) {
def respond_to? method, access = false
responds << [method, access]
@@ -334,20 +334,20 @@ class RelationTest < ActiveRecord::TestCase
end
def test_respond_to_dynamic_finders
- relation = Topic.scoped
+ relation = Topic.all
["find_by_title", "find_by_title_and_author_name", "find_or_create_by_title", "find_or_initialize_by_title_and_author_name"].each do |method|
- assert_respond_to relation, method, "Topic.scoped should respond to #{method.inspect}"
+ assert_respond_to relation, method, "Topic.all should respond to #{method.inspect}"
end
end
def test_respond_to_class_methods_and_scopes
- assert Topic.scoped.respond_to?(:by_lifo)
+ assert Topic.all.respond_to?(:by_lifo)
end
def test_find_with_readonly_option
- Developer.scoped.each { |d| assert !d.readonly? }
- Developer.scoped.readonly.each { |d| assert d.readonly? }
+ Developer.all.each { |d| assert !d.readonly? }
+ Developer.all.readonly.each { |d| assert d.readonly? }
end
def test_eager_association_loading_of_stis_with_multiple_references
@@ -396,7 +396,7 @@ class RelationTest < ActiveRecord::TestCase
end
assert_queries(2) do
- posts = Post.scoped.includes(:comments).order('posts.id')
+ posts = Post.all.includes(:comments).order('posts.id')
assert posts.first.comments.first
end
@@ -413,12 +413,12 @@ class RelationTest < ActiveRecord::TestCase
end
def test_default_scope_with_conditions_string
- assert_equal Developer.where(name: 'David').map(&:id).sort, DeveloperCalledDavid.scoped.map(&:id).sort
+ assert_equal Developer.where(name: 'David').map(&:id).sort, DeveloperCalledDavid.all.map(&:id).sort
assert_nil DeveloperCalledDavid.create!.name
end
def test_default_scope_with_conditions_hash
- assert_equal Developer.where(name: 'Jamis').map(&:id).sort, DeveloperCalledJamis.scoped.map(&:id).sort
+ assert_equal Developer.where(name: 'Jamis').map(&:id).sort, DeveloperCalledJamis.all.map(&:id).sort
assert_equal 'Jamis', DeveloperCalledJamis.create!.name
end
@@ -457,20 +457,20 @@ class RelationTest < ActiveRecord::TestCase
assert_equal expected_taggings, author.taggings.uniq.sort_by { |t| t.id }
end
- authors = Author.scoped
+ authors = Author.all
assert_equal david, authors.find_by_id_and_name(david.id, david.name)
assert_equal david, authors.find_by_id_and_name!(david.id, david.name)
end
def test_dynamic_find_by_attributes_bang
- author = Author.scoped.find_by_id!(authors(:david).id)
+ author = Author.all.find_by_id!(authors(:david).id)
assert_equal "David", author.name
- assert_raises(ActiveRecord::RecordNotFound) { Author.scoped.find_by_id_and_name!(20, 'invalid') }
+ assert_raises(ActiveRecord::RecordNotFound) { Author.all.find_by_id_and_name!(20, 'invalid') }
end
def test_find_id
- authors = Author.scoped
+ authors = Author.all
david = authors.find(authors(:david).id)
assert_equal 'David', david.name
@@ -493,14 +493,14 @@ class RelationTest < ActiveRecord::TestCase
end
def test_find_in_empty_array
- authors = Author.scoped.where(:id => [])
- assert_blank authors.all
+ authors = Author.all.where(:id => [])
+ assert_blank authors.to_a
end
def test_where_with_ar_object
author = Author.first
- authors = Author.scoped.where(:id => author)
- assert_equal 1, authors.all.length
+ authors = Author.all.where(:id => author)
+ assert_equal 1, authors.to_a.length
end
def test_find_with_list_of_ar
@@ -528,7 +528,7 @@ class RelationTest < ActiveRecord::TestCase
relation = relation.where(:name => david.name)
relation = relation.where(:name => 'Santiago')
relation = relation.where(:id => david.id)
- assert_equal [], relation.all
+ assert_equal [], relation.to_a
end
def test_multi_where_ands_queries
@@ -547,7 +547,7 @@ class RelationTest < ActiveRecord::TestCase
].inject(Author.unscoped) do |memo, param|
memo.where(param)
end
- assert_equal [], relation.all
+ assert_equal [], relation.to_a
end
def test_find_all_using_where_with_relation
@@ -556,7 +556,7 @@ class RelationTest < ActiveRecord::TestCase
# assert_queries(2) {
assert_queries(1) {
relation = Author.where(:id => Author.where(:id => david.id))
- assert_equal [david], relation.all
+ assert_equal [david], relation.to_a
}
end
@@ -566,7 +566,7 @@ class RelationTest < ActiveRecord::TestCase
# assert_queries(2) {
assert_queries(1) {
relation = Minivan.where(:minivan_id => Minivan.where(:name => cool_first.name))
- assert_equal [cool_first], relation.all
+ assert_equal [cool_first], relation.to_a
}
end
@@ -577,7 +577,7 @@ class RelationTest < ActiveRecord::TestCase
assert_queries(1) {
relation = Author.where(:id => subquery)
- assert_equal [david], relation.all
+ assert_equal [david], relation.to_a
}
assert_equal 0, subquery.select_values.size
@@ -587,7 +587,7 @@ class RelationTest < ActiveRecord::TestCase
david = authors(:david)
assert_queries(1) {
relation = Author.where(:id => Author.joins(:posts).where(:id => david.id))
- assert_equal [david], relation.all
+ assert_equal [david], relation.to_a
}
end
@@ -596,7 +596,7 @@ class RelationTest < ActiveRecord::TestCase
david = authors(:david)
assert_queries(1) {
relation = Author.where(:name => Author.where(:id => david.id).select(:name))
- assert_equal [david], relation.all
+ assert_equal [david], relation.to_a
}
end
@@ -615,7 +615,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_last
- authors = Author.scoped
+ authors = Author.all
assert_equal authors(:bob), authors.last
end
@@ -670,8 +670,8 @@ class RelationTest < ActiveRecord::TestCase
def test_relation_merging_with_eager_load
relations = []
- relations << Post.order('comments.id DESC').merge(Post.eager_load(:last_comment)).merge(Post.scoped)
- relations << Post.eager_load(:last_comment).merge(Post.order('comments.id DESC')).merge(Post.scoped)
+ relations << Post.order('comments.id DESC').merge(Post.eager_load(:last_comment)).merge(Post.all)
+ relations << Post.eager_load(:last_comment).merge(Post.order('comments.id DESC')).merge(Post.all)
relations.each do |posts|
post = posts.find { |p| p.id == 1 }
@@ -685,7 +685,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_relation_merging_with_preload
- [Post.scoped.merge(Post.preload(:author)), Post.preload(:author).merge(Post.scoped)].each do |posts|
+ [Post.all.merge(Post.preload(:author)), Post.preload(:author).merge(Post.all)].each do |posts|
assert_queries(2) { assert posts.first.author }
end
end
@@ -704,7 +704,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_count
- posts = Post.scoped
+ posts = Post.all
assert_equal 11, posts.count
assert_equal 11, posts.count(:all)
@@ -715,7 +715,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_count_with_distinct
- posts = Post.scoped
+ posts = Post.all
assert_equal 3, posts.count(:comments_count, :distinct => true)
assert_equal 11, posts.count(:comments_count, :distinct => false)
@@ -726,7 +726,7 @@ class RelationTest < ActiveRecord::TestCase
def test_count_explicit_columns
Post.update_all(:comments_count => nil)
- posts = Post.scoped
+ posts = Post.all
assert_equal [0], posts.select('comments_count').where('id is not null').group('id').order('id').count.values.uniq
assert_equal 0, posts.where('id is not null').select('comments_count').count
@@ -738,13 +738,13 @@ class RelationTest < ActiveRecord::TestCase
end
def test_multiple_selects
- post = Post.scoped.select('comments_count').select('title').order("id ASC").first
+ post = Post.all.select('comments_count').select('title').order("id ASC").first
assert_equal "Welcome to the weblog", post.title
assert_equal 2, post.comments_count
end
def test_size
- posts = Post.scoped
+ posts = Post.all
assert_queries(1) { assert_equal 11, posts.size }
assert ! posts.loaded?
@@ -790,7 +790,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_empty
- posts = Post.scoped
+ posts = Post.all
assert_queries(1) { assert_equal false, posts.empty? }
assert ! posts.loaded?
@@ -816,7 +816,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_any
- posts = Post.scoped
+ posts = Post.all
# This test was failing when run on its own (as opposed to running the entire suite).
# The second line in the assert_queries block was causing visit_Arel_Attributes_Attribute
@@ -838,7 +838,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_many
- posts = Post.scoped
+ posts = Post.all
assert_queries(2) do
assert posts.many? # Uses COUNT()
@@ -850,14 +850,14 @@ class RelationTest < ActiveRecord::TestCase
end
def test_many_with_limits
- posts = Post.scoped
+ posts = Post.all
assert posts.many?
assert ! posts.limit(1).many?
end
def test_build
- posts = Post.scoped
+ posts = Post.all
post = posts.new
assert_kind_of Post, post
@@ -872,7 +872,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_create
- birds = Bird.scoped
+ birds = Bird.all
sparrow = birds.create
assert_kind_of Bird, sparrow
@@ -884,7 +884,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_create_bang
- birds = Bird.scoped
+ birds = Bird.all
assert_raises(ActiveRecord::RecordInvalid) { birds.create! }
@@ -1026,24 +1026,24 @@ class RelationTest < ActiveRecord::TestCase
def test_except
relation = Post.where(:author_id => 1).order('id ASC').limit(1)
- assert_equal [posts(:welcome)], relation.all
+ assert_equal [posts(:welcome)], relation.to_a
author_posts = relation.except(:order, :limit)
- assert_equal Post.where(:author_id => 1).all, author_posts.all
+ assert_equal Post.where(:author_id => 1).to_a, author_posts.to_a
all_posts = relation.except(:where, :order, :limit)
- assert_equal Post.all, all_posts.all
+ assert_equal Post.to_a, all_posts.to_a
end
def test_only
relation = Post.where(:author_id => 1).order('id ASC').limit(1)
- assert_equal [posts(:welcome)], relation.all
+ assert_equal [posts(:welcome)], relation.to_a
author_posts = relation.only(:where)
- assert_equal Post.where(:author_id => 1).all, author_posts.all
+ assert_equal Post.where(:author_id => 1).to_a, author_posts.to_a
all_posts = relation.only(:limit)
- assert_equal Post.limit(1).all.first, all_posts.first
+ assert_equal Post.limit(1).to_a.first, all_posts.first
end
def test_anonymous_extension
@@ -1064,7 +1064,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_order_by_relation_attribute
- assert_equal Post.order(Post.arel_table[:title]).all, Post.order("title").all
+ assert_equal Post.order(Post.arel_table[:title]).to_a, Post.order("title").to_a
end
def test_default_scope_order_with_scope_order
@@ -1074,12 +1074,12 @@ class RelationTest < ActiveRecord::TestCase
def test_order_using_scoping
car1 = CoolCar.order('id DESC').scoping do
- CoolCar.scoped(:order => 'id asc').first
+ CoolCar.all.merge!(:order => 'id asc').first
end
assert_equal 'zyke', car1.name
car2 = FastCar.order('id DESC').scoping do
- FastCar.scoped(:order => 'id asc').first
+ FastCar.all.merge!(:order => 'id asc').first
end
assert_equal 'zyke', car2.name
end
@@ -1098,7 +1098,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_primary_key
- assert_equal "id", Post.scoped.primary_key
+ assert_equal "id", Post.all.primary_key
end
def test_eager_loading_with_conditions_on_joins
@@ -1222,7 +1222,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_presence
- topics = Topic.scoped
+ topics = Topic.all
# the first query is triggered because there are no topics yet.
assert_queries(1) { assert topics.present? }
@@ -1256,7 +1256,7 @@ class RelationTest < ActiveRecord::TestCase
end
test "find_by returns nil if the record is missing" do
- assert_equal nil, Post.scoped.find_by("1 = 0")
+ assert_equal nil, Post.all.find_by("1 = 0")
end
test "find_by doesn't have implicit ordering" do
@@ -1281,12 +1281,12 @@ class RelationTest < ActiveRecord::TestCase
test "find_by! raises RecordNotFound if the record is missing" do
assert_raises(ActiveRecord::RecordNotFound) do
- Post.scoped.find_by!("1 = 0")
+ Post.all.find_by!("1 = 0")
end
end
test "loaded relations cannot be mutated by multi value methods" do
- relation = Post.scoped
+ relation = Post.all
relation.to_a
assert_raises(ActiveRecord::ImmutableRelation) do
@@ -1295,7 +1295,7 @@ class RelationTest < ActiveRecord::TestCase
end
test "loaded relations cannot be mutated by single value methods" do
- relation = Post.scoped
+ relation = Post.all
relation.to_a
assert_raises(ActiveRecord::ImmutableRelation) do
@@ -1304,11 +1304,42 @@ class RelationTest < ActiveRecord::TestCase
end
test "loaded relations cannot be mutated by merge!" do
- relation = Post.scoped
+ relation = Post.all
relation.to_a
assert_raises(ActiveRecord::ImmutableRelation) do
relation.merge! where: 'foo'
end
end
+
+ test "relations show the records in #inspect" do
+ relation = Post.limit(2)
+ assert_equal "#<ActiveRecord::Relation [#{Post.limit(2).map(&:inspect).join(', ')}]>", relation.inspect
+ end
+
+ test "relations limit the records in #inspect at 10" do
+ relation = Post.limit(11)
+ assert_equal "#<ActiveRecord::Relation [#{Post.limit(10).map(&:inspect).join(', ')}, ...]>", relation.inspect
+ end
+
+ test "already-loaded relations don't perform a new query in #inspect" do
+ relation = Post.limit(2)
+ relation.to_a
+
+ expected = "#<ActiveRecord::Relation [#{Post.limit(2).map(&:inspect).join(', ')}]>"
+
+ assert_no_queries do
+ assert_equal expected, relation.inspect
+ end
+ end
+
+ test 'using a custom table affects the wheres' do
+ table_alias = Post.arel_table.alias('omg_posts')
+
+ relation = ActiveRecord::Relation.new Post, table_alias
+ relation.where!(:foo => "bar")
+
+ node = relation.arel.constraints.first.grep(Arel::Attributes::Attribute).first
+ assert_equal table_alias, node.relation
+ end
end
diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb
index a4c065e667..ce167509c1 100644
--- a/activerecord/test/cases/serialization_test.rb
+++ b/activerecord/test/cases/serialization_test.rb
@@ -51,4 +51,11 @@ class SerializationTest < ActiveRecord::TestCase
assert_equal @contact_attributes[:awesome], contact.awesome, "For #{format}"
end
end
+
+ def test_serialized_attributes_are_class_level_settings
+ assert_raise NoMethodError do
+ topic = Topic.new
+ topic.serialized_attributes = []
+ end
+ end
end
diff --git a/activerecord/test/cases/session_store/sql_bypass_test.rb b/activerecord/test/cases/session_store/sql_bypass_test.rb
index 6749d4ce98..b8cf4cf2cc 100644
--- a/activerecord/test/cases/session_store/sql_bypass_test.rb
+++ b/activerecord/test/cases/session_store/sql_bypass_test.rb
@@ -56,6 +56,20 @@ module ActiveRecord
s.destroy
assert_nil SqlBypass.find_by_session_id session_id
end
+
+ def test_data_column
+ SqlBypass.drop_table! if exists = Session.table_exists?
+ old, SqlBypass.data_column = SqlBypass.data_column, 'foo'
+ SqlBypass.create_table!
+
+ session_id = 20
+ SqlBypass.new(:data => 'hello', :session_id => session_id).save
+ assert_equal 'hello', SqlBypass.find_by_session_id(session_id).data
+ ensure
+ SqlBypass.drop_table!
+ SqlBypass.data_column = old
+ SqlBypass.create_table! if exists
+ end
end
end
end
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
index e401dd4b12..3e60b62fd5 100644
--- a/activerecord/test/cases/store_test.rb
+++ b/activerecord/test/cases/store_test.rb
@@ -34,6 +34,11 @@ class StoreTest < ActiveRecord::TestCase
assert @john.settings_changed?
end
+ test "updating the store won't mark it as changed if an attribute isn't changed" do
+ @john.color = @john.color
+ assert !@john.settings_changed?
+ end
+
test "object initialization with not nullable column" do
assert_equal true, @john.remember_login
end
@@ -115,4 +120,11 @@ class StoreTest < ActiveRecord::TestCase
test "stored attributes are returned" do
assert_equal [:color, :homepage], Admin::User.stored_attributes[:settings]
end
+
+ test "stores_attributes are class level settings" do
+ assert_raise NoMethodError do
+ @john.stored_attributes = {}
+ end
+ end
+
end
diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb
index 8c96a8aaa1..4f3489b7a5 100644
--- a/activerecord/test/cases/tasks/database_tasks_test.rb
+++ b/activerecord/test/cases/tasks/database_tasks_test.rb
@@ -16,6 +16,22 @@ module ActiveRecord
:postgresql => :postgresql_tasks,
:sqlite3 => :sqlite_tasks
}
+
+ class DatabaseTasksRegisterTask < ActiveRecord::TestCase
+ def test_register_task
+ klazz = Class.new do
+ def initialize(*arguments); end
+ def structure_dump(filename); end
+ end
+ instance = klazz.new
+
+ klazz.stubs(:new).returns instance
+ instance.expects(:structure_dump).with("awesome-file.sql")
+
+ ActiveRecord::Tasks::DatabaseTasks.register_task(/foo/, klazz)
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump({'adapter' => :foo}, "awesome-file.sql")
+ end
+ end
class DatabaseTasksCreateTest < ActiveRecord::TestCase
include DatabaseTasksSetupper
diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb
index 7df6993b30..7444dc5de1 100644
--- a/activerecord/test/cases/timestamp_test.rb
+++ b/activerecord/test/cases/timestamp_test.rb
@@ -11,7 +11,7 @@ class TimestampTest < ActiveRecord::TestCase
def setup
@developer = Developer.first
- @developer.update_column(:updated_at, Time.now.prev_month)
+ @developer.update_columns(updated_at: Time.now.prev_month)
@previously_updated_at = @developer.updated_at
end
@@ -133,7 +133,7 @@ class TimestampTest < ActiveRecord::TestCase
pet = Pet.first
owner = pet.owner
- owner.update_column(:happy_at, 3.days.ago)
+ owner.update_columns(happy_at: 3.days.ago)
previously_owner_updated_at = owner.updated_at
pet.name = "I'm a parrot"
@@ -153,7 +153,7 @@ class TimestampTest < ActiveRecord::TestCase
owner = pet.owner
time = 3.days.ago
- owner.update_column(:updated_at, time)
+ owner.update_columns(updated_at: time)
toy.touch
owner.reload
diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb
index c173ee9a15..46212e49b6 100644
--- a/activerecord/test/cases/validations/uniqueness_validation_test.rb
+++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb
@@ -22,6 +22,14 @@ end
class Thaumaturgist < IneptWizard
end
+class ReplyTitle; end
+
+class ReplyWithTitleObject < Reply
+ validates_uniqueness_of :content, :scope => :title
+
+ def title; ReplyTitle.new; end
+end
+
class UniquenessValidationTest < ActiveRecord::TestCase
fixtures :topics, 'warehouse-things', :developers
@@ -104,6 +112,14 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert !r2.valid?, "Saving r2 first time"
end
+ def test_validate_uniqueness_with_composed_attribute_scope
+ r1 = ReplyWithTitleObject.create "title" => "r1", "content" => "hello world"
+ assert r1.valid?, "Saving r1"
+
+ r2 = ReplyWithTitleObject.create "title" => "r1", "content" => "hello world"
+ assert !r2.valid?, "Saving r2 first time"
+ end
+
def test_validate_uniqueness_with_object_arg
Reply.validates_uniqueness_of(:topic)
@@ -189,7 +205,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert t_utf8.save, "Should save t_utf8 as unique"
# If database hasn't UTF-8 character set, this test fails
- if Topic.scoped(:select => 'LOWER(title) AS title').find(t_utf8).title == "я тоже уникальный!"
+ if Topic.all.merge!(:select => 'LOWER(title) AS title').find(t_utf8).title == "я тоже уникальный!"
t2_utf8 = Topic.new("title" => "я тоже УНИКАЛЬНЫЙ!")
assert !t2_utf8.valid?, "Shouldn't be valid"
assert !t2_utf8.save, "Shouldn't save t2_utf8 as unique"
diff --git a/activerecord/test/cases/xml_serialization_test.rb b/activerecord/test/cases/xml_serialization_test.rb
index 7249ae9e4d..ff4f1412d2 100644
--- a/activerecord/test/cases/xml_serialization_test.rb
+++ b/activerecord/test/cases/xml_serialization_test.rb
@@ -286,7 +286,7 @@ class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase
# getting appended to.
def test_modules
- projects = MyApplication::Business::Project.all
+ projects = MyApplication::Business::Project.to_a
xml = projects.to_xml
root = projects.first.class.to_s.underscore.pluralize.tr('/','_').dasherize
assert_match "<#{root} type=\"array\">", xml
diff --git a/activerecord/test/config.example.yml b/activerecord/test/config.example.yml
index f450efd839..479b8c050d 100644
--- a/activerecord/test/config.example.yml
+++ b/activerecord/test/config.example.yml
@@ -1,5 +1,7 @@
default_connection: <%= defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3' %>
+with_manual_interventions: false
+
connections:
jdbcderby:
arunit: activerecord_unittest
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index 14444a0092..3157d8fe7f 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -1,12 +1,12 @@
class Author < ActiveRecord::Base
has_many :posts
has_many :very_special_comments, :through => :posts
- has_many :posts_with_comments, :include => :comments, :class_name => "Post"
- has_many :popular_grouped_posts, :include => :comments, :class_name => "Post", :group => "type", :having => "SUM(comments_count) > 1", :select => "type"
- has_many :posts_with_comments_sorted_by_comment_id, :include => :comments, :class_name => "Post", :order => 'comments.id'
- has_many :posts_sorted_by_id_limited, :class_name => "Post", :order => 'posts.id', :limit => 1
- has_many :posts_with_categories, :include => :categories, :class_name => "Post"
- has_many :posts_with_comments_and_categories, :include => [ :comments, :categories ], :order => "posts.id", :class_name => "Post"
+ has_many :posts_with_comments, -> { includes(:comments) }, :class_name => "Post"
+ has_many :popular_grouped_posts, -> { includes(:comments).group("type").having("SUM(comments_count) > 1").select("type") }, :class_name => "Post"
+ has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order('comments.id') }, :class_name => "Post"
+ has_many :posts_sorted_by_id_limited, -> { order('posts.id').limit(1) }, :class_name => "Post"
+ has_many :posts_with_categories, -> { includes(:categories) }, :class_name => "Post"
+ has_many :posts_with_comments_and_categories, -> { includes(:comments, :categories).order("posts.id") }, :class_name => "Post"
has_many :posts_containing_the_letter_a, :class_name => "Post"
has_many :posts_with_extension, :class_name => "Post" do #, :extend => ProxyTestExtension
def testing_proxy_owner
@@ -19,28 +19,28 @@ class Author < ActiveRecord::Base
proxy_target
end
end
- has_one :post_about_thinking, :class_name => 'Post', :conditions => "posts.title like '%thinking%'"
- has_one :post_about_thinking_with_last_comment, :class_name => 'Post', :conditions => "posts.title like '%thinking%'", :include => :last_comment
+ has_one :post_about_thinking, -> { where("posts.title like '%thinking%'") }, :class_name => 'Post'
+ has_one :post_about_thinking_with_last_comment, -> { where("posts.title like '%thinking%'").includes(:last_comment) }, :class_name => 'Post'
has_many :comments, :through => :posts
has_many :comments_containing_the_letter_e, :through => :posts, :source => :comments
- has_many :comments_with_order_and_conditions, :through => :posts, :source => :comments, :order => 'comments.body', :conditions => "comments.body like 'Thank%'"
- has_many :comments_with_include, :through => :posts, :source => :comments, :include => :post
+ has_many :comments_with_order_and_conditions, -> { order('comments.body').where("comments.body like 'Thank%'") }, :through => :posts, :source => :comments
+ has_many :comments_with_include, -> { includes(:post) }, :through => :posts, :source => :comments
has_many :first_posts
- has_many :comments_on_first_posts, :through => :first_posts, :source => :comments, :order => 'posts.id desc, comments.id asc'
+ has_many :comments_on_first_posts, -> { order('posts.id desc, comments.id asc') }, :through => :first_posts, :source => :comments
has_one :first_post
- has_one :comment_on_first_post, :through => :first_post, :source => :comments, :order => 'posts.id desc, comments.id asc'
+ has_one :comment_on_first_post, -> { order('posts.id desc, comments.id asc') }, :through => :first_post, :source => :comments
- has_many :thinking_posts, :class_name => 'Post', :conditions => { :title => 'So I was thinking' }, :dependent => :delete_all
- has_many :welcome_posts, :class_name => 'Post', :conditions => { :title => 'Welcome to the weblog' }
+ has_many :thinking_posts, -> { where(:title => 'So I was thinking') }, :dependent => :delete_all, :class_name => 'Post'
+ has_many :welcome_posts, -> { where(:title => 'Welcome to the weblog') }, :class_name => 'Post'
- has_many :comments_desc, :through => :posts, :source => :comments, :order => 'comments.id DESC'
- has_many :limited_comments, :through => :posts, :source => :comments, :limit => 1
+ has_many :comments_desc, -> { order('comments.id DESC') }, :through => :posts, :source => :comments
+ has_many :limited_comments, -> { limit(1) }, :through => :posts, :source => :comments
has_many :funky_comments, :through => :posts, :source => :comments
- has_many :ordered_uniq_comments, :through => :posts, :source => :comments, :uniq => true, :order => 'comments.id'
- has_many :ordered_uniq_comments_desc, :through => :posts, :source => :comments, :uniq => true, :order => 'comments.id DESC'
- has_many :readonly_comments, :through => :posts, :source => :comments, :readonly => true
+ has_many :ordered_uniq_comments, -> { uniq.order('comments.id') }, :through => :posts, :source => :comments
+ has_many :ordered_uniq_comments_desc, -> { uniq.order('comments.id DESC') }, :through => :posts, :source => :comments
+ has_many :readonly_comments, -> { readonly }, :through => :posts, :source => :comments
has_many :special_posts
has_many :special_post_comments, :through => :special_posts, :source => :comments
@@ -48,16 +48,15 @@ class Author < ActiveRecord::Base
has_many :sti_posts, :class_name => 'StiPost'
has_many :sti_post_comments, :through => :sti_posts, :source => :comments
- has_many :special_nonexistant_posts, :class_name => "SpecialPost", :conditions => "posts.body = 'nonexistant'"
- has_many :special_nonexistant_post_comments, :through => :special_nonexistant_posts, :source => :comments, :conditions => { 'comments.post_id' => 0 }
+ has_many :special_nonexistant_posts, -> { where("posts.body = 'nonexistant'") }, :class_name => "SpecialPost"
+ has_many :special_nonexistant_post_comments, -> { where('comments.post_id' => 0) }, :through => :special_nonexistant_posts, :source => :comments
has_many :nonexistant_comments, :through => :posts
- has_many :hello_posts, :class_name => "Post", :conditions => "posts.body = 'hello'"
+ has_many :hello_posts, -> { where "posts.body = 'hello'" }, :class_name => "Post"
has_many :hello_post_comments, :through => :hello_posts, :source => :comments
- has_many :posts_with_no_comments, :class_name => 'Post', :conditions => { 'comments.id' => nil }, :include => :comments
+ has_many :posts_with_no_comments, -> { where('comments.id' => nil).includes(:comments) }, :class_name => 'Post'
- has_many :hello_posts_with_hash_conditions, :class_name => "Post",
-:conditions => {:body => 'hello'}
+ has_many :hello_posts_with_hash_conditions, -> { where(:body => 'hello') }, :class_name => "Post"
has_many :hello_post_comments_with_hash_conditions, :through =>
:hello_posts_with_hash_conditions, :source => :comments
@@ -84,29 +83,31 @@ class Author < ActiveRecord::Base
has_many :special_categories, :through => :special_categorizations, :source => :category
has_one :special_category, :through => :special_categorizations, :source => :category
- has_many :categories_like_general, :through => :categorizations, :source => :category, :class_name => 'Category', :conditions => { :name => 'General' }
+ has_many :categories_like_general, -> { where(:name => 'General') }, :through => :categorizations, :source => :category, :class_name => 'Category'
has_many :categorized_posts, :through => :categorizations, :source => :post
- has_many :unique_categorized_posts, :through => :categorizations, :source => :post, :uniq => true
+ has_many :unique_categorized_posts, -> { uniq }, :through => :categorizations, :source => :post
has_many :nothings, :through => :kateggorisatons, :class_name => 'Category'
has_many :author_favorites
- has_many :favorite_authors, :through => :author_favorites, :order => 'name'
+ has_many :favorite_authors, -> { order('name') }, :through => :author_favorites
has_many :tagging, :through => :posts
has_many :taggings, :through => :posts
has_many :tags, :through => :posts
- has_many :similar_posts, :through => :tags, :source => :tagged_posts, :uniq => true
- has_many :distinct_tags, :through => :posts, :source => :tags, :select => "DISTINCT tags.*", :order => "tags.name"
has_many :post_categories, :through => :posts, :source => :categories
has_many :tagging_tags, :through => :taggings, :source => :tag
+
+ has_many :similar_posts, -> { uniq }, :through => :tags, :source => :tagged_posts
+ has_many :distinct_tags, -> { select("DISTINCT tags.*").order("tags.name") }, :through => :posts, :source => :tags
+
has_many :tags_with_primary_key, :through => :posts
has_many :books
has_many :subscriptions, :through => :books
- has_many :subscribers, :through => :subscriptions, :order => "subscribers.nick" # through has_many :through (on through reflection)
- has_many :distinct_subscribers, :through => :subscriptions, :source => :subscriber, :select => "DISTINCT subscribers.*", :order => "subscribers.nick"
+ has_many :subscribers, -> { order("subscribers.nick") }, :through => :subscriptions
+ has_many :distinct_subscribers, -> { select("DISTINCT subscribers.*").order("subscribers.nick") }, :through => :subscriptions, :source => :subscriber
has_one :essay, :primary_key => :name, :as => :writer
has_one :essay_category, :through => :essay, :source => :category
@@ -130,12 +131,11 @@ class Author < ActiveRecord::Base
has_many :category_post_comments, :through => :categories, :source => :post_comments
- has_many :misc_posts, :class_name => 'Post',
- :conditions => { :posts => { :title => ['misc post by bob', 'misc post by mary'] } }
+ has_many :misc_posts, -> { where(:posts => { :title => ['misc post by bob', 'misc post by mary'] }) }, :class_name => 'Post'
has_many :misc_post_first_blue_tags, :through => :misc_posts, :source => :first_blue_tags
- has_many :misc_post_first_blue_tags_2, :through => :posts, :source => :first_blue_tags_2,
- :conditions => { :posts => { :title => ['misc post by bob', 'misc post by mary'] } }
+ has_many :misc_post_first_blue_tags_2, -> { where(:posts => { :title => ['misc post by bob', 'misc post by mary'] }) },
+ :through => :posts, :source => :first_blue_tags_2
has_many :posts_with_default_include, :class_name => 'PostWithDefaultInclude'
has_many :comments_on_posts_with_default_include, :through => :posts_with_default_include, :source => :comments
diff --git a/activerecord/test/models/book.rb b/activerecord/test/models/book.rb
index d27d0af77c..ce81a37966 100644
--- a/activerecord/test/models/book.rb
+++ b/activerecord/test/models/book.rb
@@ -2,7 +2,7 @@ class Book < ActiveRecord::Base
has_many :authors
has_many :citations, :foreign_key => 'book1_id'
- has_many :references, :through => :citations, :source => :reference_of, :uniq => true
+ has_many :references, -> { uniq }, :through => :citations, :source => :reference_of
has_many :subscriptions
has_many :subscribers, :through => :subscriptions
diff --git a/activerecord/test/models/bulb.rb b/activerecord/test/models/bulb.rb
index 640e57555d..0dc2fdd8ae 100644
--- a/activerecord/test/models/bulb.rb
+++ b/activerecord/test/models/bulb.rb
@@ -8,7 +8,7 @@ class Bulb < ActiveRecord::Base
after_initialize :record_scope_after_initialize
def record_scope_after_initialize
- @scope_after_initialize = self.class.scoped
+ @scope_after_initialize = self.class.all
end
after_initialize :record_attributes_after_initialize
diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb
index b4bc0ad5fa..ac42f444e1 100644
--- a/activerecord/test/models/car.rb
+++ b/activerecord/test/models/car.rb
@@ -1,11 +1,11 @@
class Car < ActiveRecord::Base
has_many :bulbs
- has_many :foo_bulbs, :class_name => "Bulb", :conditions => { :name => 'foo' }
- has_many :frickinawesome_bulbs, :class_name => "Bulb", :conditions => { :frickinawesome => true }
+ has_many :foo_bulbs, -> { where(:name => 'foo') }, :class_name => "Bulb"
+ has_many :frickinawesome_bulbs, -> { where :frickinawesome => true }, :class_name => "Bulb"
has_one :bulb
- has_one :frickinawesome_bulb, :class_name => "Bulb", :conditions => { :frickinawesome => true }
+ has_one :frickinawesome_bulb, -> { where :frickinawesome => true }, :class_name => "Bulb"
has_many :tyres
has_many :engines, :dependent => :destroy
diff --git a/activerecord/test/models/category.rb b/activerecord/test/models/category.rb
index ab3139680c..f8c8ebb70c 100644
--- a/activerecord/test/models/category.rb
+++ b/activerecord/test/models/category.rb
@@ -2,20 +2,20 @@ class Category < ActiveRecord::Base
has_and_belongs_to_many :posts
has_and_belongs_to_many :special_posts, :class_name => "Post"
has_and_belongs_to_many :other_posts, :class_name => "Post"
- has_and_belongs_to_many :posts_with_authors_sorted_by_author_id, :class_name => "Post", :include => :authors, :order => "authors.id"
+ has_and_belongs_to_many :posts_with_authors_sorted_by_author_id, -> { includes(:authors).order("authors.id") }, :class_name => "Post"
- has_and_belongs_to_many(:select_testing_posts,
+ has_and_belongs_to_many :select_testing_posts,
+ -> { select 'posts.*, 1 as correctness_marker' },
:class_name => 'Post',
:foreign_key => 'category_id',
- :association_foreign_key => 'post_id',
- :select => 'posts.*, 1 as correctness_marker')
+ :association_foreign_key => 'post_id'
has_and_belongs_to_many :post_with_conditions,
- :class_name => 'Post',
- :conditions => { :title => 'Yet Another Testing Title' }
+ -> { where :title => 'Yet Another Testing Title' },
+ :class_name => 'Post'
- has_and_belongs_to_many :popular_grouped_posts, :class_name => "Post", :group => "posts.type", :having => "sum(comments.post_id) > 2", :include => :comments
- has_and_belongs_to_many :posts_grouped_by_title, :class_name => "Post", :group => "title", :select => "title"
+ has_and_belongs_to_many :popular_grouped_posts, -> { group("posts.type").having("sum(comments.post_id) > 2").includes(:comments) }, :class_name => "Post"
+ has_and_belongs_to_many :posts_grouped_by_title, -> { group("title").select("title") }, :class_name => "Post"
def self.what_are_you
'a category...'
@@ -25,7 +25,7 @@ class Category < ActiveRecord::Base
has_many :post_comments, :through => :posts, :source => :comments
has_many :authors, :through => :categorizations
- has_many :authors_with_select, :through => :categorizations, :source => :author, :select => 'authors.*, categorizations.post_id'
+ has_many :authors_with_select, -> { select 'authors.*, categorizations.post_id' }, :through => :categorizations, :source => :author
scope :general, -> { where(:name => 'General') }
end
diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb
index 3e9f1b0635..4b2015fe01 100644
--- a/activerecord/test/models/comment.rb
+++ b/activerecord/test/models/comment.rb
@@ -4,7 +4,7 @@ class Comment < ActiveRecord::Base
scope :not_again, -> { where("comments.body NOT LIKE '%again%'") }
scope :for_first_post, -> { where(:post_id => 1) }
scope :for_first_author, -> { joins(:post).where("posts.author_id" => 1) }
- scope :created, -> { scoped }
+ scope :created, -> { all }
belongs_to :post, :counter_cache => true
has_many :ratings
@@ -19,13 +19,13 @@ class Comment < ActiveRecord::Base
end
def self.search_by_type(q)
- self.scoped(:where => ["#{QUOTED_TYPE} = ?", q]).all
+ where("#{QUOTED_TYPE} = ?", q)
end
def self.all_as_method
all
end
- scope :all_as_scope, -> { scoped }
+ scope :all_as_scope, -> { all }
end
class SpecialComment < Comment
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index 7b993d5a2c..ff6b7d085f 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -36,60 +36,47 @@ module Namespaced
end
class Firm < Company
- has_many :clients, :order => "id", :dependent => :destroy, :counter_sql =>
- "SELECT COUNT(*) FROM companies WHERE firm_id = 1 " +
- "AND (#{QUOTED_TYPE} = 'Client' OR #{QUOTED_TYPE} = 'SpecialClient' OR #{QUOTED_TYPE} = 'VerySpecialClient' )",
+ has_many :clients, -> { order "id" }, :dependent => :destroy,
:before_remove => :log_before_remove,
:after_remove => :log_after_remove
has_many :unsorted_clients, :class_name => "Client"
has_many :unsorted_clients_with_symbol, :class_name => :Client
- has_many :clients_sorted_desc, :class_name => "Client", :order => "id DESC"
- has_many :clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id"
- has_many :clients_ordered_by_name, :order => "name", :class_name => "Client"
+ has_many :clients_sorted_desc, -> { order "id DESC" }, :class_name => "Client"
+ has_many :clients_of_firm, -> { order "id" }, :foreign_key => "client_of", :class_name => "Client"
+ has_many :clients_ordered_by_name, -> { order "name" }, :class_name => "Client"
has_many :unvalidated_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :validate => false
- has_many :dependent_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :destroy
- has_many :exclusively_dependent_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all
- has_many :limited_clients, :class_name => "Client", :limit => 1
- has_many :clients_with_interpolated_conditions, :class_name => "Client", :conditions => proc { "rating > #{rating}" }
- has_many :clients_like_ms, :conditions => "name = 'Microsoft'", :class_name => "Client", :order => "id"
- has_many :clients_like_ms_with_hash_conditions, :conditions => { :name => 'Microsoft' }, :class_name => "Client", :order => "id"
- has_many :clients_using_sql, :class_name => "Client", :finder_sql => proc { "SELECT * FROM companies WHERE client_of = #{id}" }
- has_many :clients_using_counter_sql, :class_name => "Client",
- :finder_sql => proc { "SELECT * FROM companies WHERE client_of = #{id} " },
- :counter_sql => proc { "SELECT COUNT(*) FROM companies WHERE client_of = #{id}" }
- has_many :clients_using_zero_counter_sql, :class_name => "Client",
- :finder_sql => proc { "SELECT * FROM companies WHERE client_of = #{id}" },
- :counter_sql => proc { "SELECT 0 FROM companies WHERE client_of = #{id}" }
- has_many :no_clients_using_counter_sql, :class_name => "Client",
- :finder_sql => 'SELECT * FROM companies WHERE client_of = 1000',
- :counter_sql => 'SELECT COUNT(*) FROM companies WHERE client_of = 1000'
- has_many :clients_using_finder_sql, :class_name => "Client", :finder_sql => 'SELECT * FROM companies WHERE 1=1'
+ has_many :dependent_clients_of_firm, -> { order "id" }, :foreign_key => "client_of", :class_name => "Client", :dependent => :destroy
+ has_many :exclusively_dependent_clients_of_firm, -> { order "id" }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
+ has_many :limited_clients, -> { limit 1 }, :class_name => "Client"
+ has_many :clients_with_interpolated_conditions, ->(firm) { where "rating > #{firm.rating}" }, :class_name => "Client"
+ has_many :clients_like_ms, -> { where("name = 'Microsoft'").order("id") }, :class_name => "Client"
+ has_many :clients_like_ms_with_hash_conditions, -> { where(:name => 'Microsoft').order("id") }, :class_name => "Client"
has_many :plain_clients, :class_name => 'Client'
- has_many :readonly_clients, :class_name => 'Client', :readonly => true
+ has_many :readonly_clients, -> { readonly }, :class_name => 'Client'
has_many :clients_using_primary_key, :class_name => 'Client',
:primary_key => 'name', :foreign_key => 'firm_name'
has_many :clients_using_primary_key_with_delete_all, :class_name => 'Client',
:primary_key => 'name', :foreign_key => 'firm_name', :dependent => :delete_all
- has_many :clients_grouped_by_firm_id, :class_name => "Client", :group => "firm_id", :select => "firm_id"
- has_many :clients_grouped_by_name, :class_name => "Client", :group => "name", :select => "name"
+ has_many :clients_grouped_by_firm_id, -> { group("firm_id").select("firm_id") }, :class_name => "Client"
+ has_many :clients_grouped_by_name, -> { group("name").select("name") }, :class_name => "Client"
has_one :account, :foreign_key => "firm_id", :dependent => :destroy, :validate => true
has_one :unvalidated_account, :foreign_key => "firm_id", :class_name => 'Account', :validate => false
- has_one :account_with_select, :foreign_key => "firm_id", :select => "id, firm_id", :class_name=>'Account'
- has_one :readonly_account, :foreign_key => "firm_id", :class_name => "Account", :readonly => true
+ has_one :account_with_select, -> { select("id, firm_id") }, :foreign_key => "firm_id", :class_name=>'Account'
+ has_one :readonly_account, -> { readonly }, :foreign_key => "firm_id", :class_name => "Account"
# added order by id as in fixtures there are two accounts for Rails Core
# Oracle tests were failing because of that as the second fixture was selected
- has_one :account_using_primary_key, :primary_key => "firm_id", :class_name => "Account", :order => "id"
+ has_one :account_using_primary_key, -> { order('id') }, :primary_key => "firm_id", :class_name => "Account"
has_one :account_using_foreign_and_primary_keys, :foreign_key => "firm_name", :primary_key => "name", :class_name => "Account"
has_one :deletable_account, :foreign_key => "firm_id", :class_name => "Account", :dependent => :delete
- has_one :account_limit_500_with_hash_conditions, :foreign_key => "firm_id", :class_name => "Account", :conditions => { :credit_limit => 500 }
+ has_one :account_limit_500_with_hash_conditions, -> { where :credit_limit => 500 }, :foreign_key => "firm_id", :class_name => "Account"
has_one :unautosaved_account, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false
has_many :accounts
has_many :unautosaved_accounts, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false
- has_many :association_with_references, :class_name => 'Client', :references => :foo
+ has_many :association_with_references, -> { references(:foo) }, :class_name => 'Client'
def log
@log ||= []
@@ -111,20 +98,20 @@ class DependentFirm < Company
end
class RestrictedFirm < Company
- has_one :account, :foreign_key => "firm_id", :dependent => :restrict, :order => "id"
- has_many :companies, :foreign_key => 'client_of', :order => "id", :dependent => :restrict
+ has_one :account, -> { order("id") }, :foreign_key => "firm_id", :dependent => :restrict
+ has_many :companies, -> { order("id") }, :foreign_key => 'client_of', :dependent => :restrict
end
class Client < Company
belongs_to :firm, :foreign_key => "client_of"
belongs_to :firm_with_basic_id, :class_name => "Firm", :foreign_key => "firm_id"
- belongs_to :firm_with_select, :class_name => "Firm", :foreign_key => "firm_id", :select => "id"
+ belongs_to :firm_with_select, -> { select("id") }, :class_name => "Firm", :foreign_key => "firm_id"
belongs_to :firm_with_other_name, :class_name => "Firm", :foreign_key => "client_of"
- belongs_to :firm_with_condition, :class_name => "Firm", :foreign_key => "client_of", :conditions => ["1 = ?", 1]
+ belongs_to :firm_with_condition, -> { where "1 = ?", 1 }, :class_name => "Firm", :foreign_key => "client_of"
belongs_to :firm_with_primary_key, :class_name => "Firm", :primary_key => "name", :foreign_key => "firm_name"
belongs_to :firm_with_primary_key_symbols, :class_name => "Firm", :primary_key => :name, :foreign_key => :firm_name
- belongs_to :readonly_firm, :class_name => "Firm", :foreign_key => "firm_id", :readonly => true
- belongs_to :bob_firm, :class_name => "Firm", :foreign_key => "client_of", :conditions => { :name => "Bob" }
+ belongs_to :readonly_firm, -> { readonly }, :class_name => "Firm", :foreign_key => "firm_id"
+ belongs_to :bob_firm, -> { where :name => "Bob" }, :class_name => "Firm", :foreign_key => "client_of"
has_many :accounts, :through => :firm
belongs_to :account
@@ -179,9 +166,9 @@ end
class ExclusivelyDependentFirm < Company
has_one :account, :foreign_key => "firm_id", :dependent => :delete
- has_many :dependent_sanitized_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => "name = 'BigShot Inc.'"
- has_many :dependent_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => ["name = ?", 'BigShot Inc.']
- has_many :dependent_hash_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => {:name => 'BigShot Inc.'}
+ has_many :dependent_sanitized_conditional_clients_of_firm, -> { order("id").where("name = 'BigShot Inc.'") }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
+ has_many :dependent_conditional_clients_of_firm, -> { order("id").where("name = ?", 'BigShot Inc.') }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
+ has_many :dependent_hash_conditional_clients_of_firm, -> { order("id").where(:name => 'BigShot Inc.') }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
end
class SpecialClient < Client
diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb
index 2c8c30efb4..0d14fa1be1 100644
--- a/activerecord/test/models/company_in_module.rb
+++ b/activerecord/test/models/company_in_module.rb
@@ -7,11 +7,10 @@ module MyApplication
end
class Firm < Company
- has_many :clients, :order => "id", :dependent => :destroy
- has_many :clients_sorted_desc, :class_name => "Client", :order => "id DESC"
- has_many :clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id"
- has_many :clients_like_ms, :conditions => "name = 'Microsoft'", :class_name => "Client", :order => "id"
- has_many :clients_using_sql, :class_name => "Client", :finder_sql => 'SELECT * FROM companies WHERE client_of = #{id}'
+ has_many :clients, -> { order("id") }, :dependent => :destroy
+ has_many :clients_sorted_desc, -> { order("id DESC") }, :class_name => "Client"
+ has_many :clients_of_firm, -> { order "id" }, :foreign_key => "client_of", :class_name => "Client"
+ has_many :clients_like_ms, -> { where("name = 'Microsoft'").order("id") }, :class_name => "Client"
has_one :account, :class_name => 'MyApplication::Billing::Account', :dependent => :destroy
end
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index 43cde4ab73..adf4c56294 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -2,42 +2,42 @@ require 'ostruct'
module DeveloperProjectsAssociationExtension
def find_most_recent
- scoped(:order => "id DESC").first
+ order("id DESC").first
end
end
module DeveloperProjectsAssociationExtension2
def find_least_recent
- scoped(:order => "id ASC").first
+ order("id ASC").first
end
end
class Developer < ActiveRecord::Base
has_and_belongs_to_many :projects do
def find_most_recent
- scoped(:order => "id DESC").first
+ order("id DESC").first
end
end
has_and_belongs_to_many :projects_extended_by_name,
+ -> { extending(DeveloperProjectsAssociationExtension) },
:class_name => "Project",
:join_table => "developers_projects",
- :association_foreign_key => "project_id",
- :extend => DeveloperProjectsAssociationExtension
+ :association_foreign_key => "project_id"
has_and_belongs_to_many :projects_extended_by_name_twice,
+ -> { extending(DeveloperProjectsAssociationExtension, DeveloperProjectsAssociationExtension2) },
:class_name => "Project",
:join_table => "developers_projects",
- :association_foreign_key => "project_id",
- :extend => [DeveloperProjectsAssociationExtension, DeveloperProjectsAssociationExtension2]
+ :association_foreign_key => "project_id"
has_and_belongs_to_many :projects_extended_by_name_and_block,
+ -> { extending(DeveloperProjectsAssociationExtension) },
:class_name => "Project",
:join_table => "developers_projects",
- :association_foreign_key => "project_id",
- :extend => DeveloperProjectsAssociationExtension do
+ :association_foreign_key => "project_id" do
def find_least_recent
- scoped(:order => "id ASC").first
+ order("id ASC").first
end
end
@@ -175,14 +175,14 @@ end
class EagerDeveloperWithDefaultScope < ActiveRecord::Base
self.table_name = 'developers'
- has_and_belongs_to_many :projects, :foreign_key => 'developer_id', :join_table => 'developers_projects', :order => 'projects.id'
+ has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
default_scope { includes(:projects) }
end
class EagerDeveloperWithClassMethodDefaultScope < ActiveRecord::Base
self.table_name = 'developers'
- has_and_belongs_to_many :projects, :foreign_key => 'developer_id', :join_table => 'developers_projects', :order => 'projects.id'
+ has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
def self.default_scope
includes(:projects)
@@ -191,21 +191,21 @@ end
class EagerDeveloperWithLambdaDefaultScope < ActiveRecord::Base
self.table_name = 'developers'
- has_and_belongs_to_many :projects, :foreign_key => 'developer_id', :join_table => 'developers_projects', :order => 'projects.id'
+ has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
default_scope lambda { includes(:projects) }
end
class EagerDeveloperWithBlockDefaultScope < ActiveRecord::Base
self.table_name = 'developers'
- has_and_belongs_to_many :projects, :foreign_key => 'developer_id', :join_table => 'developers_projects', :order => 'projects.id'
+ has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
default_scope { includes(:projects) }
end
class EagerDeveloperWithCallableDefaultScope < ActiveRecord::Base
self.table_name = 'developers'
- has_and_belongs_to_many :projects, :foreign_key => 'developer_id', :join_table => 'developers_projects', :order => 'projects.id'
+ has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
default_scope OpenStruct.new(:call => includes(:projects))
end
diff --git a/activerecord/test/models/liquid.rb b/activerecord/test/models/liquid.rb
index 3fcd5e4b69..6cfd443e75 100644
--- a/activerecord/test/models/liquid.rb
+++ b/activerecord/test/models/liquid.rb
@@ -1,5 +1,5 @@
class Liquid < ActiveRecord::Base
self.table_name = :liquid
- has_many :molecules, :uniq => true
+ has_many :molecules, -> { uniq }
end
diff --git a/activerecord/test/models/member.rb b/activerecord/test/models/member.rb
index 1f719b0858..359b29fac3 100644
--- a/activerecord/test/models/member.rb
+++ b/activerecord/test/models/member.rb
@@ -5,8 +5,8 @@ class Member < ActiveRecord::Base
has_many :fellow_members, :through => :club, :source => :members
has_one :club, :through => :current_membership
has_one :selected_club, :through => :selected_membership, :source => :club
- has_one :favourite_club, :through => :membership, :conditions => ["memberships.favourite = ?", true], :source => :club
- has_one :hairy_club, :through => :membership, :conditions => {:clubs => {:name => "Moustache and Eyebrow Fancier Club"}}, :source => :club
+ has_one :favourite_club, -> { where "memberships.favourite = ?", true }, :through => :membership, :source => :club
+ has_one :hairy_club, -> { where :clubs => {:name => "Moustache and Eyebrow Fancier Club"} }, :through => :membership, :source => :club
has_one :sponsor, :as => :sponsorable
has_one :sponsor_club, :through => :sponsor
has_one :member_detail
@@ -27,7 +27,7 @@ class Member < ActiveRecord::Base
has_many :current_memberships
has_one :club_through_many, :through => :current_memberships, :source => :club
- has_many :current_memberships, :conditions => { :favourite => true }
+ has_many :current_memberships, -> { where :favourite => true }
has_many :clubs, :through => :current_memberships
end
diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb
index 33cd6020a1..e204508986 100644
--- a/activerecord/test/models/person.rb
+++ b/activerecord/test/models/person.rb
@@ -5,14 +5,14 @@ class Person < ActiveRecord::Base
has_many :posts, :through => :readers
has_many :secure_posts, :through => :secure_readers
- has_many :posts_with_no_comments, :through => :readers, :source => :post, :include => :comments,
- :conditions => 'comments.id is null', :references => :comments
+ has_many :posts_with_no_comments, -> { includes(:comments).where('comments.id is null').references(:comments) },
+ :through => :readers, :source => :post
has_many :references
has_many :bad_references
- has_many :fixed_bad_references, :conditions => { :favourite => true }, :class_name => 'BadReference'
- has_one :favourite_reference, :class_name => 'Reference', :conditions => ['favourite=?', true]
- has_many :posts_with_comments_sorted_by_comment_id, :through => :readers, :source => :post, :include => :comments, :order => 'comments.id'
+ has_many :fixed_bad_references, -> { where :favourite => true }, :class_name => 'BadReference'
+ has_one :favourite_reference, -> { where 'favourite=?', true }, :class_name => 'Reference'
+ has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order('comments.id') }, :through => :readers, :source => :post
has_many :jobs, :through => :references
has_many :jobs_with_dependent_destroy, :source => :job, :through => :references, :dependent => :destroy
@@ -109,4 +109,4 @@ class NestedPerson < ActiveRecord::Base
def best_friend_first_name=(new_name)
assign_attributes({ :best_friend_attributes => { :first_name => new_name } })
end
-end \ No newline at end of file
+end
diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb
index 5e0f5323e6..609b9369a9 100644
--- a/activerecord/test/models/pirate.rb
+++ b/activerecord/test/models/pirate.rb
@@ -1,7 +1,7 @@
class Pirate < ActiveRecord::Base
belongs_to :parrot, :validate => true
belongs_to :non_validated_parrot, :class_name => 'Parrot'
- has_and_belongs_to_many :parrots, :validate => true, :order => 'parrots.id ASC'
+ has_and_belongs_to_many :parrots, -> { order('parrots.id ASC') }, :validate => true
has_and_belongs_to_many :non_validated_parrots, :class_name => 'Parrot'
has_and_belongs_to_many :parrots_with_method_callbacks, :class_name => "Parrot",
:before_add => :log_before_add,
@@ -21,7 +21,7 @@ class Pirate < ActiveRecord::Base
has_one :ship
has_one :update_only_ship, :class_name => 'Ship'
has_one :non_validated_ship, :class_name => 'Ship'
- has_many :birds, :order => 'birds.id ASC'
+ has_many :birds, -> { order('birds.id ASC') }
has_many :birds_with_method_callbacks, :class_name => "Bird",
:before_add => :log_before_add,
:after_add => :log_after_add,
@@ -34,7 +34,7 @@ class Pirate < ActiveRecord::Base
:after_remove => proc {|p,b| p.ship_log << "after_removing_proc_bird_#{b.id}"}
has_many :birds_with_reject_all_blank, :class_name => "Bird"
- has_one :foo_bulb, :foreign_key => :car_id, :class_name => "Bulb", :conditions => { :name => 'foo' }
+ has_one :foo_bulb, -> { where :name => 'foo' }, :foreign_key => :car_id, :class_name => "Bulb"
accepts_nested_attributes_for :parrots, :birds, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 1aaf9a1b82..9c5b7310ff 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -16,14 +16,14 @@ class Post < ActiveRecord::Base
end
end
- belongs_to :author_with_posts, :class_name => "Author", :foreign_key => :author_id, :include => :posts
- belongs_to :author_with_address, :class_name => "Author", :foreign_key => :author_id, :include => :author_address
+ belongs_to :author_with_posts, -> { includes(:posts) }, :class_name => "Author", :foreign_key => :author_id
+ belongs_to :author_with_address, -> { includes(:author_address) }, :class_name => "Author", :foreign_key => :author_id
def first_comment
super.body
end
- has_one :first_comment, :class_name => 'Comment', :order => 'id ASC'
- has_one :last_comment, :class_name => 'Comment', :order => 'id desc'
+ has_one :first_comment, -> { order('id ASC') }, :class_name => 'Comment'
+ has_one :last_comment, -> { order('id desc') }, :class_name => 'Comment'
scope :with_special_comments, -> { joins(:comments).where(:comments => {:type => 'SpecialComment'}) }
scope :with_very_special_comments, -> { joins(:comments).where(:comments => {:type => 'VerySpecialComment'}) }
@@ -31,7 +31,7 @@ class Post < ActiveRecord::Base
has_many :comments do
def find_most_recent
- scoped(:order => "id DESC").first
+ order("id DESC").first
end
def newest
@@ -47,13 +47,14 @@ class Post < ActiveRecord::Base
has_many :author_categorizations, :through => :author, :source => :categorizations
has_many :author_addresses, :through => :author
- has_many :comments_with_interpolated_conditions, :class_name => 'Comment',
- :conditions => proc { ["#{"#{aliased_table_name}." rescue ""}body = ?", 'Thank you for the welcome'] }
+ has_many :comments_with_interpolated_conditions,
+ ->(p) { where "#{"#{p.aliased_table_name}." rescue ""}body = ?", 'Thank you for the welcome' },
+ :class_name => 'Comment'
has_one :very_special_comment
- has_one :very_special_comment_with_post, :class_name => "VerySpecialComment", :include => :post
+ has_one :very_special_comment_with_post, -> { includes(:post) }, :class_name => "VerySpecialComment"
has_many :special_comments
- has_many :nonexistant_comments, :class_name => 'Comment', :conditions => 'comments.id < 0'
+ has_many :nonexistant_comments, -> { where 'comments.id < 0' }, :class_name => 'Comment'
has_many :special_comments_ratings, :through => :special_comments, :source => :ratings
has_many :special_comments_ratings_taggings, :through => :special_comments_ratings, :source => :taggings
@@ -64,33 +65,30 @@ class Post < ActiveRecord::Base
has_many :taggings, :as => :taggable
has_many :tags, :through => :taggings do
def add_joins_and_select
- scoped(:select => 'tags.*, authors.id as author_id',
- :joins => 'left outer join posts on taggings.taggable_id = posts.id left outer join authors on posts.author_id = authors.id').all
+ select('tags.*, authors.id as author_id')
+ .joins('left outer join posts on taggings.taggable_id = posts.id left outer join authors on posts.author_id = authors.id')
+ .to_a
end
end
- has_many :interpolated_taggings, :class_name => 'Tagging', :as => :taggable, :conditions => proc { "1 = #{1}" }
- has_many :interpolated_tags, :through => :taggings
- has_many :interpolated_tags_2, :through => :interpolated_taggings, :source => :tag
-
has_many :taggings_with_delete_all, :class_name => 'Tagging', :as => :taggable, :dependent => :delete_all
has_many :taggings_with_destroy, :class_name => 'Tagging', :as => :taggable, :dependent => :destroy
has_many :tags_with_destroy, :through => :taggings, :source => :tag, :dependent => :destroy
has_many :tags_with_nullify, :through => :taggings, :source => :tag, :dependent => :nullify
- has_many :misc_tags, :through => :taggings, :source => :tag, :conditions => { :tags => { :name => 'Misc' } }
+ has_many :misc_tags, -> { where :tags => { :name => 'Misc' } }, :through => :taggings, :source => :tag
has_many :funky_tags, :through => :taggings, :source => :tag
has_many :super_tags, :through => :taggings
has_many :tags_with_primary_key, :through => :taggings, :source => :tag_with_primary_key
has_one :tagging, :as => :taggable
- has_many :first_taggings, :as => :taggable, :class_name => 'Tagging', :conditions => { :taggings => { :comment => 'first' } }
- has_many :first_blue_tags, :through => :first_taggings, :source => :tag, :conditions => { :tags => { :name => 'Blue' } }
+ has_many :first_taggings, -> { where :taggings => { :comment => 'first' } }, :as => :taggable, :class_name => 'Tagging'
+ has_many :first_blue_tags, -> { where :tags => { :name => 'Blue' } }, :through => :first_taggings, :source => :tag
- has_many :first_blue_tags_2, :through => :taggings, :source => :blue_tag, :conditions => { :taggings => { :comment => 'first' } }
+ has_many :first_blue_tags_2, -> { where :taggings => { :comment => 'first' } }, :through => :taggings, :source => :blue_tag
- has_many :invalid_taggings, :as => :taggable, :class_name => "Tagging", :conditions => 'taggings.id < 0'
+ has_many :invalid_taggings, -> { where 'taggings.id < 0' }, :as => :taggable, :class_name => "Tagging"
has_many :invalid_tags, :through => :invalid_taggings, :source => :tag
has_many :categorizations, :foreign_key => :category_id
@@ -109,7 +107,7 @@ class Post < ActiveRecord::Base
has_many :readers
has_many :secure_readers
- has_many :readers_with_person, :include => :person, :class_name => "Reader"
+ has_many :readers_with_person, -> { includes(:person) }, :class_name => "Reader"
has_many :people, :through => :readers
has_many :secure_people, :through => :secure_readers
has_many :single_people, :through => :readers
@@ -118,7 +116,7 @@ class Post < ActiveRecord::Base
:after_add => lambda {|owner, reader| log(:added, :after, reader.first_name) },
:before_remove => lambda {|owner, reader| log(:removed, :before, reader.first_name) },
:after_remove => lambda {|owner, reader| log(:removed, :after, reader.first_name) }
- has_many :skimmers, :class_name => 'Reader', :conditions => { :skimmer => true }
+ has_many :skimmers, -> { where :skimmer => true }, :class_name => 'Reader'
has_many :impatient_people, :through => :skimmers, :source => :person
def self.top(limit)
diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb
index 32ce164995..6b53ead34d 100644
--- a/activerecord/test/models/project.rb
+++ b/activerecord/test/models/project.rb
@@ -1,26 +1,17 @@
class Project < ActiveRecord::Base
- has_and_belongs_to_many :developers, :uniq => true, :order => 'developers.name desc, developers.id desc'
- has_and_belongs_to_many :readonly_developers, :class_name => "Developer", :readonly => true
- has_and_belongs_to_many :selected_developers, :class_name => "Developer", :select => "developers.*", :uniq => true
- has_and_belongs_to_many :non_unique_developers, :order => 'developers.name desc, developers.id desc', :class_name => 'Developer'
- has_and_belongs_to_many :limited_developers, :class_name => "Developer", :limit => 1
- has_and_belongs_to_many :developers_named_david, :class_name => "Developer", :conditions => "name = 'David'", :uniq => true
- has_and_belongs_to_many :developers_named_david_with_hash_conditions, :class_name => "Developer", :conditions => { :name => 'David' }, :uniq => true
- has_and_belongs_to_many :salaried_developers, :class_name => "Developer", :conditions => "salary > 0"
- has_and_belongs_to_many :developers_with_finder_sql, :class_name => "Developer", :finder_sql => proc { "SELECT t.*, j.* FROM developers_projects j, developers t WHERE t.id = j.developer_id AND j.project_id = #{id} ORDER BY t.id" }
- has_and_belongs_to_many :developers_with_multiline_finder_sql, :class_name => "Developer", :finder_sql => proc {
- "SELECT
- t.*, j.*
- FROM
- developers_projects j,
- developers t WHERE t.id = j.developer_id AND j.project_id = #{id} ORDER BY t.id"
- }
- has_and_belongs_to_many :developers_by_sql, :class_name => "Developer", :delete_sql => proc { |record| "DELETE FROM developers_projects WHERE project_id = #{id} AND developer_id = #{record.id}" }
+ has_and_belongs_to_many :developers, -> { uniq.order 'developers.name desc, developers.id desc' }
+ has_and_belongs_to_many :readonly_developers, -> { readonly }, :class_name => "Developer"
+ has_and_belongs_to_many :selected_developers, -> { uniq.select "developers.*" }, :class_name => "Developer"
+ has_and_belongs_to_many :non_unique_developers, -> { order 'developers.name desc, developers.id desc' }, :class_name => 'Developer'
+ has_and_belongs_to_many :limited_developers, -> { limit 1 }, :class_name => "Developer"
+ has_and_belongs_to_many :developers_named_david, -> { where("name = 'David'").uniq }, :class_name => "Developer"
+ has_and_belongs_to_many :developers_named_david_with_hash_conditions, -> { where(:name => 'David').uniq }, :class_name => "Developer"
+ has_and_belongs_to_many :salaried_developers, -> { where "salary > 0" }, :class_name => "Developer"
has_and_belongs_to_many :developers_with_callbacks, :class_name => "Developer", :before_add => Proc.new {|o, r| o.developers_log << "before_adding#{r.id || '<new>'}"},
:after_add => Proc.new {|o, r| o.developers_log << "after_adding#{r.id || '<new>'}"},
:before_remove => Proc.new {|o, r| o.developers_log << "before_removing#{r.id}"},
:after_remove => Proc.new {|o, r| o.developers_log << "after_removing#{r.id}"}
- has_and_belongs_to_many :well_payed_salary_groups, :class_name => "Developer", :group => "developers.salary", :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary"
+ has_and_belongs_to_many :well_payed_salary_groups, -> { group("developers.salary").having("SUM(salary) > 10000").select("SUM(salary) as salary") }, :class_name => "Developer"
attr_accessor :developers_log
after_initialize :set_developers_log
@@ -32,7 +23,7 @@ class Project < ActiveRecord::Base
def self.all_as_method
all
end
- scope :all_as_scope, -> { scoped }
+ scope :all_as_scope, -> { all }
end
class SpecialProject < Project
diff --git a/activerecord/test/models/sponsor.rb b/activerecord/test/models/sponsor.rb
index aa4a3638fd..ec3dcf8a97 100644
--- a/activerecord/test/models/sponsor.rb
+++ b/activerecord/test/models/sponsor.rb
@@ -2,6 +2,6 @@ class Sponsor < ActiveRecord::Base
belongs_to :sponsor_club, :class_name => "Club", :foreign_key => "club_id"
belongs_to :sponsorable, :polymorphic => true
belongs_to :thing, :polymorphic => true, :foreign_type => :sponsorable_type, :foreign_key => :sponsorable_id
- belongs_to :sponsorable_with_conditions, :polymorphic => true,
- :foreign_type => 'sponsorable_type', :foreign_key => 'sponsorable_id', :conditions => {:name => 'Ernie'}
+ belongs_to :sponsorable_with_conditions, -> { where :name => 'Ernie'}, :polymorphic => true,
+ :foreign_type => 'sponsorable_type', :foreign_key => 'sponsorable_id'
end
diff --git a/activerecord/test/models/tagging.rb b/activerecord/test/models/tagging.rb
index ef323df158..f91f2ad2e9 100644
--- a/activerecord/test/models/tagging.rb
+++ b/activerecord/test/models/tagging.rb
@@ -3,12 +3,11 @@ module Taggable
end
class Tagging < ActiveRecord::Base
- belongs_to :tag, :include => :tagging
+ belongs_to :tag, -> { includes(:tagging) }
belongs_to :super_tag, :class_name => 'Tag', :foreign_key => 'super_tag_id'
belongs_to :invalid_tag, :class_name => 'Tag', :foreign_key => 'tag_id'
- belongs_to :blue_tag, :class_name => 'Tag', :foreign_key => :tag_id, :conditions => { :tags => { :name => 'Blue' } }
+ belongs_to :blue_tag, -> { where :tags => { :name => 'Blue' } }, :class_name => 'Tag', :foreign_key => :tag_id
belongs_to :tag_with_primary_key, :class_name => 'Tag', :foreign_key => :tag_id, :primary_key => :custom_primary_key
- belongs_to :interpolated_tag, :class_name => 'Tag', :foreign_key => :tag_id, :conditions => proc { "1 = #{1}" }
belongs_to :taggable, :polymorphic => true, :counter_cache => true
has_many :things, :through => :taggable
end
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index fb27c9d4f0..4b27c16681 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -1,5 +1,5 @@
class Topic < ActiveRecord::Base
- scope :base, -> { scoped }
+ scope :base, -> { all }
scope :written_before, lambda { |time|
if time
where 'written_on < ?', time
@@ -8,13 +8,13 @@ class Topic < ActiveRecord::Base
scope :approved, -> { where(:approved => true) }
scope :rejected, -> { where(:approved => false) }
- scope :scope_with_lambda, lambda { scoped }
+ scope :scope_with_lambda, lambda { all }
scope :by_lifo, -> { where(:author_name => 'lifo') }
scope :replied, -> { where 'replies_count > 0' }
scope 'approved_as_string', -> { where(:approved => true) }
- scope :anonymous_extension, -> { scoped } do
+ scope :anonymous_extension, -> { all } do
def one
1
end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 00688eab37..f48bab721f 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -37,10 +37,10 @@ ActiveRecord::Schema.define do
create_table :admin_users, :force => true do |t|
t.string :name
- t.text :settings, :null => true
+ t.string :settings, :null => true, :limit => 1024
# MySQL does not allow default values for blobs. Fake it out with a
# big varchar below.
- t.string :preferences, :null => false, :default => '', :limit => 1024
+ t.string :preferences, :null => true, :default => '', :limit => 1024
t.string :json_data, :null => true, :limit => 1024
t.string :json_data_empty, :null => false, :default => "", :limit => 1024
t.references :account
@@ -178,7 +178,7 @@ ActiveRecord::Schema.define do
t.integer :client_of
t.integer :rating, :default => 1
t.integer :account_id
- t.string :description, :null => false, :default => ""
+ t.string :description, :default => ""
end
add_index :companies, [:firm_id, :type, :rating, :ruby_type], :name => "company_index"
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index df144dd00b..cf32e2d7a0 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,5 +1,7 @@
## Rails 4.0.0 (unreleased) ##
+* `Object#try` will now return nil instead of raise a NoMethodError if the receiving object does not implement the method, but you can still get the old behavior by using the new `Object#try!` *DHH*
+
* `Time#change` now works with time values with offsets other than UTC or the local time zone. *Andrew White*
* `ActiveSupport::Callbacks`: deprecate usage of filter object with `#before` and `#after` methods as `around` callback. *Bogdan Gusiev*
diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec
index fa38d5c1e3..836bc2f9cf 100644
--- a/activesupport/activesupport.gemspec
+++ b/activesupport/activesupport.gemspec
@@ -22,4 +22,5 @@ Gem::Specification.new do |s|
s.add_dependency('i18n', '~> 0.6')
s.add_dependency('multi_json', '~> 1.3')
s.add_dependency('tzinfo', '~> 0.3.33')
+ s.add_dependency('minitest', '~> 3.2')
end
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 6cc875c69a..8f79334159 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -78,7 +78,7 @@ module ActiveSupport
private
# A hook invoked everytime a before callback is halted.
- # This can be overriden in AS::Callback implementors in order
+ # This can be overridden in AS::Callback implementors in order
# to provide better debugging/logging.
def halted_callback_hook(filter)
end
diff --git a/activesupport/lib/active_support/core_ext/date.rb b/activesupport/lib/active_support/core_ext/date.rb
new file mode 100644
index 0000000000..465fedda80
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/date.rb
@@ -0,0 +1,5 @@
+require 'active_support/core_ext/date/acts_like'
+require 'active_support/core_ext/date/calculations'
+require 'active_support/core_ext/date/conversions'
+require 'active_support/core_ext/date/zones'
+
diff --git a/activesupport/lib/active_support/core_ext/date_time.rb b/activesupport/lib/active_support/core_ext/date_time.rb
new file mode 100644
index 0000000000..e8a27b9f38
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/date_time.rb
@@ -0,0 +1,4 @@
+require 'active_support/core_ext/date_time/acts_like'
+require 'active_support/core_ext/date_time/calculations'
+require 'active_support/core_ext/date_time/conversions'
+require 'active_support/core_ext/date_time/zones'
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb
index 16a799ec03..9974b61078 100644
--- a/activesupport/lib/active_support/core_ext/object/try.rb
+++ b/activesupport/lib/active_support/core_ext/object/try.rb
@@ -5,6 +5,9 @@ class Object
# *Unlike* that method however, a +NoMethodError+ exception will *not* be raised
# and +nil+ will be returned instead, if the receiving object is a +nil+ object or NilClass.
#
+ # This is also true if the receiving object does not implemented the tried method. It will
+ # return +nil+ in that case as well.
+ #
# If try is called without a method to call, it will yield any given block with the object.
#
# Please also note that +try+ is defined on +Object+, therefore it won't work with
@@ -31,6 +34,16 @@ class Object
if a.empty? && block_given?
yield self
else
+ public_send(*a, &b) if respond_to?(a.first)
+ end
+ end
+
+ # Same as #try, but will raise a NoMethodError exception if the receiving is not nil and
+ # does not implemented the tried method.
+ def try!(*a, &b)
+ if a.empty? && block_given?
+ yield self
+ else
public_send(*a, &b)
end
end
@@ -50,4 +63,8 @@ class NilClass
def try(*args)
nil
end
+
+ def try!(*args)
+ nil
+ end
end
diff --git a/activesupport/lib/active_support/core_ext/time.rb b/activesupport/lib/active_support/core_ext/time.rb
new file mode 100644
index 0000000000..32cffe237d
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/time.rb
@@ -0,0 +1,5 @@
+require 'active_support/core_ext/time/acts_like'
+require 'active_support/core_ext/time/calculations'
+require 'active_support/core_ext/time/conversions'
+require 'active_support/core_ext/time/marshal'
+require 'active_support/core_ext/time/zones'
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index 0a71fc117c..d0f574f2ba 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -1,6 +1,7 @@
require 'active_support/duration'
require 'active_support/core_ext/time/conversions'
require 'active_support/time_with_zone'
+require 'active_support/core_ext/time/zones'
class Time
COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb
index e48866abe3..37bc3fae24 100644
--- a/activesupport/lib/active_support/core_ext/time/zones.rb
+++ b/activesupport/lib/active_support/core_ext/time/zones.rb
@@ -1,4 +1,3 @@
-require 'active_support/core_ext/time/calculations'
require 'active_support/time_with_zone'
class Time
@@ -27,11 +26,11 @@ class Time
# around_filter :set_time_zone
#
# def set_time_zone
- # old_time_zone = Time.zone
- # Time.zone = current_user.time_zone if logged_in?
- # yield
- # ensure
- # Time.zone = old_time_zone
+ # if logged_in?
+ # Time.use_zone(current_user.time_zone) { yield }
+ # else
+ # yield
+ # end
# end
# end
def zone=(time_zone)
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index 3e6c8893e9..5fd20673d8 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -1,12 +1,46 @@
require 'active_support/core_ext/hash/keys'
module ActiveSupport
- # This class has dubious semantics and we only have it so that
- # people can write <tt>params[:key]</tt> instead of <tt>params['key']</tt>
- # and they get the same value for both keys.
+ # Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered to be the same.
+ #
+ # rgb = ActiveSupport::HashWithIndifferentAccess.new
+ #
+ # rgb[:black] = '#000000'
+ # rgb[:black] # => '#000000'
+ # rgb['black'] # => '#000000'
+ #
+ # rgb['white'] = '#FFFFFF'
+ # rgb[:white] # => '#FFFFFF'
+ # rgb['white'] # => '#FFFFFF'
+ #
+ # Internally symbols are mapped to strings when used as keys in the entire
+ # writing interface (calling <tt>[]=</tt>, <tt>merge</tt>, etc). This
+ # mapping belongs to the public interface. For example, given
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new(:a => 1)
+ #
+ # you are guaranteed that the key is returned as a string:
+ #
+ # hash.keys # => ["a"]
+ #
+ # Technically other types of keys are accepted:
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new(:a => 1)
+ # hash[0] = 0
+ # hash # => {"a"=>1, 0=>0}
+ #
+ # but this class is intended for use cases where strings or symbols are the
+ # expected keys and it is convenient to understand both as the same. For
+ # example the +params+ hash in Ruby on Rails.
+ #
+ # Note that core extensions define <tt>Hash#with_indifferent_access</tt>:
+ #
+ # rgb = {:black => '#000000', :white => '#FFFFFF'}.with_indifferent_access
+ #
+ # which may be handy.
class HashWithIndifferentAccess < Hash
-
- # Always returns true, so that <tt>Array#extract_options!</tt> finds members of this class.
+ # Returns true so that <tt>Array#extract_options!</tt> finds members of
+ # this class.
def extractable_options?
true
end
@@ -51,25 +85,32 @@ module ActiveSupport
# Assigns a new value to the hash:
#
- # hash = HashWithIndifferentAccess.new
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
# hash[:key] = "value"
#
+ # This value can be later fetched using either +:key+ or +"key"+.
def []=(key, value)
regular_writer(convert_key(key), convert_value(value))
end
alias_method :store, :[]=
- # Updates the instantized hash with values from the second:
+ # Updates the receiver in-place merging in the hash passed as argument:
#
- # hash_1 = HashWithIndifferentAccess.new
- # hash_1[:key] = "value"
+ # hash_1 = ActiveSupport::HashWithIndifferentAccess.new
+ # hash_2[:key] = "value"
#
- # hash_2 = HashWithIndifferentAccess.new
+ # hash_2 = ActiveSupport::HashWithIndifferentAccess.new
# hash_2[:key] = "New Value!"
#
# hash_1.update(hash_2) # => {"key"=>"New Value!"}
#
+ # The argument can be either an
+ # <tt>ActiveSupport::HashWithIndifferentAccess</tt> or a regular +Hash+.
+ # In either case the merge respects the semantics of indifferent access.
+ #
+ # If the argument is a regular hash with keys +:key+ and +"key"+ only one
+ # of the values end up in the receiver, but which was is unespecified.
def update(other_hash)
if other_hash.is_a? HashWithIndifferentAccess
super(other_hash)
@@ -83,10 +124,10 @@ module ActiveSupport
# Checks the hash for a key matching the argument passed in:
#
- # hash = HashWithIndifferentAccess.new
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
# hash["key"] = "value"
- # hash.key? :key # => true
- # hash.key? "key" # => true
+ # hash.key?(:key) # => true
+ # hash.key?("key") # => true
#
def key?(key)
super(convert_key(key))
@@ -96,14 +137,24 @@ module ActiveSupport
alias_method :has_key?, :key?
alias_method :member?, :key?
- # Fetches the value for the specified key, same as doing hash[key]
+ # Same as <tt>Hash#fetch</tt> where the key passed as argument can be
+ # either a string or a symbol:
+ #
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
+ # counters[:foo] = 1
+ #
+ # counters.fetch("foo") # => 1
+ # counters.fetch(:bar, 0) # => 0
+ # counters.fetch(:bar) {|key| 0} # => 0
+ # counters.fetch(:zoo) # => KeyError: key not found: "zoo"
+ #
def fetch(key, *extras)
super(convert_key(key), *extras)
end
# Returns an array of the values at the specified indices:
#
- # hash = HashWithIndifferentAccess.new
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
# hash[:a] = "x"
# hash[:b] = "y"
# hash.values_at("a", "b") # => ["x", "y"]
@@ -119,23 +170,30 @@ module ActiveSupport
end
end
- # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash.
- # Does not overwrite the existing hash.
+ # This method has the same semantics of +update+, except it does not
+ # modify the receiver but rather returns a new hash with indifferent
+ # access with the result of the merge.
def merge(hash)
self.dup.update(hash)
end
- # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
- # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a <tt>HashWithDifferentAccess</tt>.
+ # Like +merge+ but the other way around: Merges the receiver into the
+ # argument and returns a new hash with indifferent access as result:
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
+ # hash['a'] = nil
+ # hash.reverse_merge(:a => 0, :b => 1) # => {"a"=>nil, "b"=>1}
+ #
def reverse_merge(other_hash)
- super self.class.new_from_hash_copying_default(other_hash)
+ super(self.class.new_from_hash_copying_default(other_hash))
end
+ # Same semantics as +reverse_merge+ but modifies the receiver in-place.
def reverse_merge!(other_hash)
replace(reverse_merge( other_hash ))
end
- # Removes a specified key from the hash.
+ # Removes the specified key from the hash.
def delete(key)
super(convert_key(key))
end
@@ -150,7 +208,7 @@ module ActiveSupport
def deep_symbolize_keys; to_hash.deep_symbolize_keys end
def to_options!; self end
- # Convert to a Hash with String keys.
+ # Convert to a regular hash with string keys.
def to_hash
Hash.new(default).merge!(self)
end
diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb
index c319e94bc6..389df58ec4 100644
--- a/activesupport/lib/active_support/json/encoding.rb
+++ b/activesupport/lib/active_support/json/encoding.rb
@@ -161,38 +161,67 @@ class Struct #:nodoc:
end
class TrueClass
- def as_json(options = nil) self end #:nodoc:
- def encode_json(encoder) to_s end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ self
+ end
+
+ def encode_json(encoder) #:nodoc:
+ to_s
+ end
end
class FalseClass
- def as_json(options = nil) self end #:nodoc:
- def encode_json(encoder) to_s end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ self
+ end
+
+ def encode_json(encoder) #:nodoc:
+ to_s
+ end
end
class NilClass
- def as_json(options = nil) self end #:nodoc:
- def encode_json(encoder) 'null' end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ self
+ end
+
+ def encode_json(encoder) #:nodoc:
+ 'null'
+ end
end
class String
- def as_json(options = nil) self end #:nodoc:
- def encode_json(encoder) encoder.escape(self) end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ self
+ end
+
+ def encode_json(encoder) #:nodoc:
+ encoder.escape(self)
+ end
end
class Symbol
- def as_json(options = nil) to_s end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ to_s
+ end
end
class Numeric
- def as_json(options = nil) self end #:nodoc:
- def encode_json(encoder) to_s end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ self
+ end
+
+ def encode_json(encoder) #:nodoc:
+ to_s
+ end
end
class Float
# Encoding Infinity or NaN to JSON should return "null". The default returns
# "Infinity" or "NaN" which breaks parsing the JSON. E.g. JSON.parse('[NaN]').
- def as_json(options = nil) finite? ? self : nil end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ finite? ? self : nil
+ end
end
class BigDecimal
@@ -216,7 +245,9 @@ class BigDecimal
end
class Regexp
- def as_json(options = nil) to_s end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ to_s
+ end
end
module Enumerable
@@ -226,7 +257,9 @@ module Enumerable
end
class Range
- def as_json(options = nil) to_s end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ to_s
+ end
end
class Array
@@ -262,7 +295,7 @@ class Hash
Hash[subset.map { |k, v| [k.to_s, encoder.as_json(v, options)] }]
end
- def encode_json(encoder)
+ def encode_json(encoder) #:nodoc:
# values are encoded with use_options = false, because we don't want hash representations from ActiveModel to be
# processed once again with as_json with options, as this could cause unexpected results (i.e. missing fields);
diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb
index 5e080df518..9cd8ac36bc 100644
--- a/activesupport/lib/active_support/tagged_logging.rb
+++ b/activesupport/lib/active_support/tagged_logging.rb
@@ -16,7 +16,7 @@ module ActiveSupport
module Formatter # :nodoc:
# This method is invoked when a log event occurs
def call(severity, timestamp, progname, msg)
- super(severity, timestamp, progname, "#{tags_text}#{msg}")
+ super(severity, timestamp, progname, "#{tags_text} #{msg}".lstrip)
end
def clear!
@@ -31,7 +31,7 @@ module ActiveSupport
def tags_text
tags = current_tags
if tags.any?
- tags.collect { |tag| "[#{tag}] " }.join
+ tags.collect { |tag| "[#{tag}]" }.join(' ')
end
end
end
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index e2b46a235a..d2b8e602f9 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -1,24 +1,21 @@
+gem 'minitest' # make sure we get the gem, not stdlib
require 'minitest/spec'
require 'active_support/testing/setup_and_teardown'
require 'active_support/testing/assertions'
require 'active_support/testing/deprecation'
-require 'active_support/testing/declarative'
require 'active_support/testing/isolation'
-require 'active_support/testing/mochaing'
+require 'active_support/testing/mocha_module'
require 'active_support/core_ext/kernel/reporting'
+require 'active_support/deprecation'
module ActiveSupport
class TestCase < ::MiniTest::Spec
- if MiniTest::Unit::VERSION < '2.6.1'
- class << self
- alias :name :to_s
- end
- end
+ include ActiveSupport::Testing::MochaModule
# Use AS::TestCase for the base class when describing a model
register_spec_type(self) do |desc|
- desc < ActiveRecord::Model
+ Class === desc && desc < ActiveRecord::Model
end
Assertion = MiniTest::Assertion
@@ -38,7 +35,24 @@ module ActiveSupport
include ActiveSupport::Testing::SetupAndTeardown
include ActiveSupport::Testing::Assertions
include ActiveSupport::Testing::Deprecation
- extend ActiveSupport::Testing::Declarative
+
+ def self.describe(text)
+ if block_given?
+ super
+ else
+ ActiveSupport::Deprecation.warn("`describe` without a block is deprecated, please switch to: `def self.name; #{text.inspect}; end`\n")
+
+ class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
+ def self.name
+ "#{text}"
+ end
+ RUBY_EVAL
+ end
+ end
+
+ class << self
+ alias :test :it
+ end
# test/unit backwards compatibility methods
alias :assert_raise :assert_raises
diff --git a/activesupport/lib/active_support/testing/declarative.rb b/activesupport/lib/active_support/testing/declarative.rb
deleted file mode 100644
index 508e37254a..0000000000
--- a/activesupport/lib/active_support/testing/declarative.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-module ActiveSupport
- module Testing
- module Declarative
-
- def self.extended(klass) #:nodoc:
- klass.class_eval do
-
- unless method_defined?(:describe)
- def self.describe(text)
- class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- def self.name
- "#{text}"
- end
- RUBY_EVAL
- end
- end
-
- end
- end
-
- unless defined?(Spec)
- # test "verify something" do
- # ...
- # end
- def test(name, &block)
- test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
- defined = instance_method(test_name) rescue false
- raise "#{test_name} is already defined in #{self}" if defined
- if block_given?
- define_method(test_name, &block)
- else
- define_method(test_name) do
- flunk "No implementation provided for #{name}"
- end
- end
- end
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/testing/mocha_module.rb b/activesupport/lib/active_support/testing/mocha_module.rb
new file mode 100644
index 0000000000..ed2942d23a
--- /dev/null
+++ b/activesupport/lib/active_support/testing/mocha_module.rb
@@ -0,0 +1,22 @@
+module ActiveSupport
+ module Testing
+ module MochaModule
+ begin
+ require 'mocha_standalone'
+ include Mocha::API
+
+ def before_setup
+ mocha_setup
+ super
+ end
+
+ def after_teardown
+ super
+ mocha_verify
+ mocha_teardown
+ end
+ rescue LoadError
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/testing/mochaing.rb b/activesupport/lib/active_support/testing/mochaing.rb
deleted file mode 100644
index 4ad75a6681..0000000000
--- a/activesupport/lib/active_support/testing/mochaing.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-begin
- silence_warnings { require 'mocha' }
-rescue LoadError
- # Fake Mocha::ExpectationError so we can rescue it in #run. Bleh.
- Object.const_set :Mocha, Module.new
- Mocha.const_set :ExpectationError, Class.new(StandardError)
-end \ No newline at end of file
diff --git a/activesupport/lib/active_support/testing/setup_and_teardown.rb b/activesupport/lib/active_support/testing/setup_and_teardown.rb
index 527fa555b7..a65148cf1f 100644
--- a/activesupport/lib/active_support/testing/setup_and_teardown.rb
+++ b/activesupport/lib/active_support/testing/setup_and_teardown.rb
@@ -4,20 +4,11 @@ require 'active_support/callbacks'
module ActiveSupport
module Testing
module SetupAndTeardown
-
- PASSTHROUGH_EXCEPTIONS = [
- NoMemoryError,
- SignalException,
- Interrupt,
- SystemExit
- ]
-
extend ActiveSupport::Concern
included do
include ActiveSupport::Callbacks
define_callbacks :setup, :teardown
-
end
module ClassMethods
@@ -30,28 +21,15 @@ module ActiveSupport
end
end
- def run(runner)
- result = '.'
- begin
- run_callbacks :setup do
- result = super
- end
- rescue *PASSTHROUGH_EXCEPTIONS
- raise
- rescue Exception => e
- result = runner.puke(self.class, method_name, e)
- ensure
- begin
- run_callbacks :teardown
- rescue *PASSTHROUGH_EXCEPTIONS
- raise
- rescue Exception => e
- result = runner.puke(self.class, method_name, e)
- end
- end
- result
+ def before_setup
+ super
+ run_callbacks :setup
end
+ def after_teardown
+ run_callbacks :teardown
+ super
+ end
end
end
end
diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb
index 57ed4a6b60..25ed962c23 100644
--- a/activesupport/test/abstract_unit.rb
+++ b/activesupport/test/abstract_unit.rb
@@ -18,8 +18,6 @@ end
require 'minitest/autorun'
require 'empty_bool'
-silence_warnings { require 'mocha' }
-
ENV['NO_RELOAD'] = '1'
require 'active_support'
diff --git a/activesupport/test/core_ext/object_and_class_ext_test.rb b/activesupport/test/core_ext/object_and_class_ext_test.rb
index 98ab82609e..ec7dd6d4fb 100644
--- a/activesupport/test/core_ext/object_and_class_ext_test.rb
+++ b/activesupport/test/core_ext/object_and_class_ext_test.rb
@@ -99,13 +99,25 @@ class ObjectTryTest < ActiveSupport::TestCase
def test_nonexisting_method
method = :undefined_method
assert !@string.respond_to?(method)
- assert_raise(NoMethodError) { @string.try(method) }
+ assert_nil @string.try(method)
end
def test_nonexisting_method_with_arguments
method = :undefined_method
assert !@string.respond_to?(method)
- assert_raise(NoMethodError) { @string.try(method, 'llo', 'y') }
+ assert_nil @string.try(method, 'llo', 'y')
+ end
+
+ def test_nonexisting_method_bang
+ method = :undefined_method
+ assert !@string.respond_to?(method)
+ assert_raise(NoMethodError) { @string.try!(method) }
+ end
+
+ def test_nonexisting_method_with_arguments_bang
+ method = :undefined_method
+ assert !@string.respond_to?(method)
+ assert_raise(NoMethodError) { @string.try!(method, 'llo', 'y') }
end
def test_valid_method
@@ -139,6 +151,18 @@ class ObjectTryTest < ActiveSupport::TestCase
assert_equal false, ran
end
+ def test_try_with_private_method_bang
+ klass = Class.new do
+ private
+
+ def private_method
+ 'private method'
+ end
+ end
+
+ assert_raise(NoMethodError) { klass.new.try!(:private_method) }
+ end
+
def test_try_with_private_method
klass = Class.new do
private
@@ -148,6 +172,6 @@ class ObjectTryTest < ActiveSupport::TestCase
end
end
- assert_raise(NoMethodError) { klass.new.try(:private_method) }
+ assert_nil klass.new.try(:private_method)
end
end
diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb
index a8d69d0ec3..ef289692bc 100644
--- a/activesupport/test/multibyte_chars_test.rb
+++ b/activesupport/test/multibyte_chars_test.rb
@@ -110,7 +110,7 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
end
%w{capitalize downcase lstrip reverse rstrip swapcase upcase}.each do |method|
- class_eval(<<-EOTESTS)
+ class_eval(<<-EOTESTS, __FILE__, __LINE__ + 1)
def test_#{method}_bang_should_return_self_when_modifying_wrapped_string
chars = ' él piDió Un bUen café '
assert_equal chars.object_id, chars.send("#{method}!").object_id
diff --git a/ci/travis.rb b/ci/travis.rb
index fc120f80ba..b03ac4fe35 100755
--- a/ci/travis.rb
+++ b/ci/travis.rb
@@ -100,9 +100,6 @@ ENV['GEM'].split(',').each do |gem|
build = Build.new(gem, :isolated => isolated)
results[build.key] = build.run!
- if build.activerecord?
- results[build.key] = build.run!
- end
end
end
diff --git a/guides/code/getting_started/app/views/comments/_comment.html.erb b/guides/code/getting_started/app/views/comments/_comment.html.erb
index 0cebe0bd96..3d2bc1590e 100644
--- a/guides/code/getting_started/app/views/comments/_comment.html.erb
+++ b/guides/code/getting_started/app/views/comments/_comment.html.erb
@@ -10,6 +10,6 @@
<p>
<%= link_to 'Destroy Comment', [comment.post, comment],
- :confirm => 'Are you sure?',
- :method => :delete %>
+ :method => :delete,
+ :data => { :confirm => 'Are you sure?' } %>
</p>
diff --git a/guides/code/getting_started/app/views/posts/index.html.erb b/guides/code/getting_started/app/views/posts/index.html.erb
index 7b72720d50..9a0e90eadc 100644
--- a/guides/code/getting_started/app/views/posts/index.html.erb
+++ b/guides/code/getting_started/app/views/posts/index.html.erb
@@ -17,7 +17,7 @@
<td><%= post.text %></td>
<td><%= link_to 'Show', :action => :show, :id => post.id %>
<td><%= link_to 'Edit', :action => :edit, :id => post.id %>
- <td><%= link_to 'Destroy', { :action => :destroy, :id => post.id }, :method => :delete, :confirm => 'Are you sure?' %>
+ <td><%= link_to 'Destroy', { :action => :destroy, :id => post.id }, :method => :delete, :data => { :confirm => 'Are you sure?' } %>
</tr>
<% end %>
</table>
diff --git a/guides/code/getting_started/test/test_helper.rb b/guides/code/getting_started/test/test_helper.rb
index 8bf1192ffe..3daca18a71 100644
--- a/guides/code/getting_started/test/test_helper.rb
+++ b/guides/code/getting_started/test/test_helper.rb
@@ -3,7 +3,7 @@ require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
class ActiveSupport::TestCase
- # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
+ # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
#
# Note: You'll currently still have to declare fixtures explicitly in integration tests
# -- they do not yet inherit this setting
diff --git a/guides/source/2_2_release_notes.textile b/guides/source/2_2_release_notes.textile
index 3a0f2efbaf..eb4b32329b 100644
--- a/guides/source/2_2_release_notes.textile
+++ b/guides/source/2_2_release_notes.textile
@@ -118,9 +118,9 @@ h4. Transactional Migrations
Historically, multiple-step Rails migrations have been a source of trouble. If something went wrong during a migration, everything before the error changed the database and everything after the error wasn't applied. Also, the migration version was stored as having been executed, which means that it couldn't be simply rerun by +rake db:migrate:redo+ after you fix the problem. Transactional migrations change this by wrapping migration steps in a DDL transaction, so that if any of them fail, the entire migration is undone. In Rails 2.2, transactional migrations are supported on PostgreSQL out of the box. The code is extensible to other database types in the future - and IBM has already extended it to support the DB2 adapter.
-* Lead Contributor: "Adam Wiggins":http://adam.blog.heroku.com/
+* Lead Contributor: "Adam Wiggins":http://adam.heroku.com/
* More information:
-** "DDL Transactions":http://adam.blog.heroku.com/past/2008/9/3/ddl_transactions/
+** "DDL Transactions":http://adam.heroku.com/past/2008/9/3/ddl_transactions/
** "A major milestone for DB2 on Rails":http://db2onrails.com/2008/11/08/a-major-milestone-for-db2-on-rails/
h4. Connection Pooling
diff --git a/guides/source/2_3_release_notes.textile b/guides/source/2_3_release_notes.textile
index 15abba66ab..36f425574b 100644
--- a/guides/source/2_3_release_notes.textile
+++ b/guides/source/2_3_release_notes.textile
@@ -561,7 +561,7 @@ This will layer the changes from the template on top of whatever code the projec
h4. Quieter Backtraces
-Building on Thoughtbot's "Quiet Backtrace":http://www.thoughtbot.com/projects/quietbacktrace plugin, which allows you to selectively remove lines from +Test::Unit+ backtraces, Rails 2.3 implements +ActiveSupport::BacktraceCleaner+ and +Rails::BacktraceCleaner+ in core. This supports both filters (to perform regex-based substitutions on backtrace lines) and silencers (to remove backtrace lines entirely). Rails automatically adds silencers to get rid of the most common noise in a new application, and builds a +config/backtrace_silencers.rb+ file to hold your own additions. This feature also enables prettier printing from any gem in the backtrace.
+Building on Thoughtbot's "Quiet Backtrace":https://github.com/thoughtbot/quietbacktrace plugin, which allows you to selectively remove lines from +Test::Unit+ backtraces, Rails 2.3 implements +ActiveSupport::BacktraceCleaner+ and +Rails::BacktraceCleaner+ in core. This supports both filters (to perform regex-based substitutions on backtrace lines) and silencers (to remove backtrace lines entirely). Rails automatically adds silencers to get rid of the most common noise in a new application, and builds a +config/backtrace_silencers.rb+ file to hold your own additions. This feature also enables prettier printing from any gem in the backtrace.
h4. Faster Boot Time in Development Mode with Lazy Loading/Autoload
diff --git a/guides/source/4_0_release_notes.textile b/guides/source/4_0_release_notes.textile
index e1d6b42e6c..d545798f6f 100644
--- a/guides/source/4_0_release_notes.textile
+++ b/guides/source/4_0_release_notes.textile
@@ -112,6 +112,8 @@ h4(#railties_deprecations). Deprecations
h3. Action Mailer
+* Allow to set default Action Mailer options via <tt>config.action_mailer.default_options=</tt>.
+
* Raise an <tt>ActionView::MissingTemplate</tt> exception when no implicit template could be found.
* Asynchronously send messages via the Rails Queue.
@@ -120,6 +122,16 @@ h3. Action Pack
h4. Action Controller
+* Add <tt>ActionController::Flash.add_flash_types</tt> method to allow people to register their own flash types. e.g.:
+
+<ruby>
+class ApplicationController
+ add_flash_types :error, :warning
+end
+</ruby>
+
+If you add the above code, you can use <tt><%= error %></tt> in an erb, and <tt>redirect_to /foo, :error => 'message'</tt> in a controller.
+
* Remove Active Model dependency from Action Pack.
* Support unicode characters in routes. Route will be automatically escaped, so instead of manually escaping:
@@ -184,6 +196,8 @@ h5(#actioncontroller_deprecations). Deprecations
h4. Action Dispatch
+* Show routes in exception page while debugging a <tt>RoutingError</tt> in development.
+
* Include <tt>mounted_helpers</tt> (helpers for accessing mounted engines) in <tt>ActionDispatch::IntegrationTest</tt> by default.
* Added <tt>ActionDispatch::SSL</tt> middleware that when included force all the requests to be under HTTPS protocol.
@@ -329,6 +343,20 @@ Moved into a separate gem <tt>sprockets-rails</tt>.
h3. Active Record
+* Add <tt>add_reference</tt> and <tt>remove_reference</tt> schema statements. Aliases, <tt>add_belongs_to</tt> and <tt>remove_belongs_to</tt> are acceptable. References are reversible.
+
+<ruby>
+# Create a user_id column
+add_reference(:products, :user)
+
+# Create a supplier_id, supplier_type columns and appropriate index
+add_reference(:products, :supplier, polymorphic: true, index: true)
+
+# Remove polymorphic reference
+remove_reference(:products, :supplier, polymorphic: true)
+</ruby>
+
+
* Add <tt>:default</tt> and <tt>:null</tt> options to <tt>column_exists?</tt>.
<ruby>
@@ -336,14 +364,19 @@ column_exists?(:testings, :taggable_id, :integer, null: false)
column_exists?(:testings, :taggable_type, :string, default: 'Photo')
</ruby>
-* <tt>ActiveRelation#inspect</tt> no longer calls <tt>#to_a</tt>. This means that in places where <tt>#inspect</tt> is implied (such as in the console), creating a relation will not execute it anymore, you'll have to call <tt>#to_a</tt> when necessary:
+* <tt>ActiveRecord::Relation#inspect</tt> now makes it clear that you are dealing with a <tt>Relation</tt> object rather than an array:
<ruby>
-User.where(:age => 30) # => returns the relation
-User.where(:age => 30).to_a # => executes the query and returns the loaded objects, as before
+User.where(:age => 30).inspect
+# => <ActiveRecord::Relation [#<User ...>, #<User ...>]>
+
+User.where(:age => 30).to_a.inspect
+# => [#<User ...>, #<User ...>]
</ruby>
-* Add <tt>collation</tt> and <tt>ctype</tt> support to PostgreSQL. These are available for PostgreSQL 8.4 or later.
+if more than 10 items are returned by the relation, inspect will only show the first 10 followed by ellipsis.
+
+* Add <tt>:collation</tt> and <tt>:ctype</tt> support to PostgreSQL. These are available for PostgreSQL 8.4 or later.
<yaml>
development:
@@ -506,7 +539,7 @@ Post.find_by! name: 'Spartacus'
* Added <tt>ActiveRecord::Base#slice</tt> to return a hash of the given methods with their names as keys and returned values as values.
-* Remove IdentityMap - IdentityMap has never graduated to be an "enabled-by-default" feature, due to some inconsistencies with associations, as described in this commit: https://github.com/rails/rails/commit/302c912bf6bcd0fa200d964ec2dc4a44abe328a6. Hence the removal from the codebase, until such issues are fixed.
+* Remove IdentityMap - IdentityMap has never graduated to be an "enabled-by-default" feature, due to some inconsistencies with associations, as described in this "commit":https://github.com/rails/rails/commit/302c912bf6bcd0fa200d964ec2dc4a44abe328a6. Hence the removal from the codebase, until such issues are fixed.
* Added a feature to dump/load internal state of +SchemaCache+ instance because we want to boot more quickly when we have many models.
@@ -666,6 +699,8 @@ where(...).remove_conditions # => still has conditions
* New rails application would be generated with the config.active_record.dependent_restrict_raises = false in the application config.
+* The migration generator now creates a join table with (commented) indexes every time the migration name contains the word "join_table".
+
h3. Active Model
* Changed <tt>AM::Serializers::JSON.include_root_in_json</tt> default value to false. Now, AM Serializers and AR objects have the same default behaviour.
@@ -708,7 +743,7 @@ h4(#activemodel_deprecations). Deprecations
h3. Active Resource
-* Active Resource is removed from Rails 4.0 and is now a separate gem. TODO: put a link to the gem here.
+* Active Resource is removed from Rails 4.0 and is now a separate "gem":https://github.com/rails/activeresource.
h3. Active Support
diff --git a/guides/source/action_mailer_basics.textile b/guides/source/action_mailer_basics.textile
index 7c61cc4a8d..54e55d7260 100644
--- a/guides/source/action_mailer_basics.textile
+++ b/guides/source/action_mailer_basics.textile
@@ -508,8 +508,8 @@ class UserMailerTest < ActionMailer::TestCase
# Test the body of the sent email contains what we expect it to
assert_equal [user.email], email.to
assert_equal "Welcome to My Awesome Site", email.subject
- assert_match(/<h1>Welcome to example.com, #{user.name}<\/h1>/, email.encoded)
- assert_match(/Welcome to example.com, #{user.name}/, email.encoded)
+ assert_match "<h1>Welcome to example.com, #{user.name}</h1>", email.body.to_s
+ assert_match "you have joined to example.com community", email.body.to_s
end
end
</ruby>
diff --git a/guides/source/active_record_querying.textile b/guides/source/active_record_querying.textile
index 101988c59e..b13932e8cb 100644
--- a/guides/source/active_record_querying.textile
+++ b/guides/source/active_record_querying.textile
@@ -12,8 +12,6 @@ This guide covers different ways to retrieve data from the database using Active
endprologue.
-WARNING. This Guide is based on Rails 3.0. Some of the code shown here will not work in other versions of Rails.
-
If you're used to using raw SQL to find database records, then you will generally find that there are better ways to carry out the same operations in Rails. Active Record insulates you from the need to use SQL in most cases.
Code examples throughout this guide will refer to one or more of the following models:
@@ -53,20 +51,26 @@ h3. Retrieving Objects from the Database
To retrieve objects from the database, Active Record provides several finder methods. Each finder method allows you to pass arguments into it to perform certain queries on your database without writing raw SQL.
The methods are:
-* +where+
-* +select+
+* +bind+
+* +create_with+
+* +extending+
+* +from+
* +group+
-* +order+
-* +reorder+
-* +reverse_order+
-* +limit+
-* +offset+
-* +joins+
+* +having+
* +includes+
+* +joins+
+* +limit+
* +lock+
+* +offset+
+* +order+
+* +none+
* +readonly+
-* +from+
-* +having+
+* +references+
+* +reorder+
+* +reverse_order+
+* +select+
+* +uniq+
+* +where+
All of the above methods return an instance of <tt>ActiveRecord::Relation</tt>.
@@ -1039,7 +1043,7 @@ Even though Active Record lets you specify conditions on the eager loaded associ
However if you must do this, you may use +where+ as you would normally.
<ruby>
-Post.includes(:comments).where("comments.visible", true)
+Post.includes(:comments).where("comments.visible" => true)
</ruby>
This would generate a query which contains a +LEFT OUTER JOIN+ whereas the +joins+ method would generate one using the +INNER JOIN+ function instead.
@@ -1128,21 +1132,6 @@ Using a class method is the preferred way to accept arguments for scopes. These
category.posts.created_before(time)
</ruby>
-h4. Working with scopes
-
-Where a relational object is required, the +scoped+ method may come in handy. This will return an +ActiveRecord::Relation+ object which can have further scoping applied to it afterwards. A place where this may come in handy is on associations
-
-<ruby>
-client = Client.find_by_first_name("Ryan")
-orders = client.orders.scoped
-</ruby>
-
-With this new +orders+ object, we are able to ascertain that this object can have more scopes applied to it. For instance, if we wanted to return orders only in the last 30 days at a later point.
-
-<ruby>
-orders.where("created_at > ?", 30.days.ago)
-</ruby>
-
h4. Applying a default scope
If we wish for a scope to be applied across all queries to the model we can use the +default_scope+ method within the model itself.
diff --git a/guides/source/active_record_validations_callbacks.textile b/guides/source/active_record_validations_callbacks.textile
index da3a96d84e..cf7293bd9e 100644
--- a/guides/source/active_record_validations_callbacks.textile
+++ b/guides/source/active_record_validations_callbacks.textile
@@ -86,6 +86,7 @@ The following methods skip validations, and will save the object to the database
* +update_all+
* +update_attribute+
* +update_column+
+* +update_columns+
* +update_counters+
Note that +save+ also has the ability to skip validations if passed +:validate => false+ as argument. This technique should be used with caution.
@@ -1084,6 +1085,7 @@ Just as with validations, it is also possible to skip callbacks. These methods s
* +toggle+
* +touch+
* +update_column+
+* +update_columns+
* +update_all+
* +update_counters+
diff --git a/guides/source/active_support_core_extensions.textile b/guides/source/active_support_core_extensions.textile
index 99b8bdd3fd..66de6fd310 100644
--- a/guides/source/active_support_core_extensions.textile
+++ b/guides/source/active_support_core_extensions.textile
@@ -2783,27 +2783,7 @@ The method +assert_valid_keys+ receives an arbitrary number of arguments, and ch
{:a => 1}.assert_valid_keys("a") # ArgumentError
</ruby>
-Active Record does not accept unknown options when building associations for example. It implements that control via +assert_valid_keys+:
-
-<ruby>
-mattr_accessor :valid_keys_for_has_many_association
-@@valid_keys_for_has_many_association = [
- :class_name, :table_name, :foreign_key, :primary_key,
- :dependent,
- :select, :conditions, :include, :order, :group, :having, :limit, :offset,
- :as, :through, :source, :source_type,
- :uniq,
- :finder_sql, :counter_sql,
- :before_add, :after_add, :before_remove, :after_remove,
- :extend, :readonly,
- :validate, :inverse_of
-]
-
-def create_has_many_reflection(association_id, options, &extension)
- options.assert_valid_keys(valid_keys_for_has_many_association)
- ...
-end
-</ruby>
+Active Record does not accept unknown options when building associations, for example. It implements that control via +assert_valid_keys+.
NOTE: Defined in +active_support/core_ext/hash/keys.rb+.
diff --git a/guides/source/ajax_on_rails.textile b/guides/source/ajax_on_rails.textile
index e23fdf9a74..26e0270a31 100644
--- a/guides/source/ajax_on_rails.textile
+++ b/guides/source/ajax_on_rails.textile
@@ -129,7 +129,7 @@ will produce
<ruby>
button_to 'Delete Image', { action: 'delete', id: @image.id },
- confirm: 'Are you sure?', method: :delete
+ method: :delete, data: { confirm: 'Are you sure?' }
</ruby>
will produce
@@ -144,8 +144,8 @@ will produce
</html>
<ruby>
-button_to 'Destroy', 'http://www.example.com', confirm: 'Are you sure?',
- method: 'delete', remote: true, data: { disable_with: 'loading...' }
+button_to 'Destroy', 'http://www.example.com',
+ method: 'delete', remote: true, data: { disable_with: 'loading...', confirm: 'Are you sure?' }
</ruby>
will produce
@@ -217,7 +217,6 @@ link_to_remote "Delete the item",
Note that if we wouldn't override the default behavior (POST), the above snippet would route to the create action rather than destroy.
** *JavaScript filters* You can customize the remote call further by wrapping it with some JavaScript code. Let's say in the previous example, when deleting a link, you'd like to ask for a confirmation by showing a simple modal text box to the user. This is a typical example what you can accomplish with these options - let's see them one by one:
-*** +:confirm+ =&gt; +msg+ Pops up a JavaScript confirmation dialog, displaying +msg+. If the user chooses 'OK', the request is launched, otherwise canceled.
*** +:condition+ =&gt; +code+ Evaluates +code+ (which should evaluate to a boolean) and proceeds if it's true, cancels the request otherwise.
*** +:before+ =&gt; +code+ Evaluates the +code+ just before launching the request. The output of the code has no influence on the execution. Typically used show a progress indicator (see this in action in the next example).
*** +:after+ =&gt; +code+ Evaluates the +code+ after launching the request. Note that this is different from the +:success+ or +:complete+ callback (covered in the next section) since those are triggered after the request is completed, while the code snippet passed to +:after+ is evaluated after the remote call is made. A common example is to disable elements on the page or otherwise prevent further action while the request is completed.
@@ -307,4 +306,4 @@ JavaScript testing reminds me the definition of the world 'classic' by Mark Twai
* Cucumber+Webrat
* Mention stuff like screw.unit/jsSpec
-Note to self: check out the RailsConf JS testing video \ No newline at end of file
+Note to self: check out the RailsConf JS testing video
diff --git a/guides/source/association_basics.textile b/guides/source/association_basics.textile
index 8ddc56bef1..4dca7a508c 100644
--- a/guides/source/association_basics.textile
+++ b/guides/source/association_basics.textile
@@ -1257,10 +1257,8 @@ The +has_many+ association supports these options:
* +:autosave+
* +:class_name+
* +:conditions+
-* +:counter_sql+
* +:dependent+
* +:extend+
-* +:finder_sql+
* +:foreign_key+
* +:group+
* +:include+
@@ -1326,12 +1324,6 @@ class Customer < ActiveRecord::Base
end
</ruby>
-h6(#has_many-counter_sql). +:counter_sql+
-
-Normally Rails automatically generates the proper SQL to count the association members. With the +:counter_sql+ option, you can specify a complete SQL statement to count them yourself.
-
-NOTE: If you specify +:finder_sql+ but not +:counter_sql+, then the counter SQL will be generated by substituting the +SELECT ... FROM+ clause of your +:finder_sql+ statement by +SELECT COUNT(*) FROM+.
-
h6(#has_many-dependent). +:dependent+
If you set the +:dependent+ option to +:destroy+, then deleting this object will call the +destroy+ method on the associated objects to delete those objects. If you set the +:dependent+ option to +:delete_all+, then deleting this object will delete the associated objects _without_ calling their +destroy+ method. If you set the +:dependent+ option to +:nullify+, then deleting this object will set the foreign key in the associated objects to +NULL+.
@@ -1345,10 +1337,6 @@ h6(#has_many-extend). +:extend+
The +:extend+ option specifies a named module to extend the association proxy. Association extensions are discussed in detail <a href="#association-extensions">later in this guide</a>.
-h6(#has_many-finder_sql). +:finder_sql+
-
-Normally Rails automatically generates the proper SQL to fetch the association members. With the +:finder_sql+ option, you can specify a complete SQL statement to fetch them yourself. If fetching objects requires complex multi-table SQL, this may be necessary.
-
h6(#has_many-foreign_key). +:foreign_key+
By convention, Rails assumes that the column used to hold the foreign key on the other model is the name of this model with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly:
@@ -1700,14 +1688,10 @@ The +has_and_belongs_to_many+ association supports these options:
* +:autosave+
* +:class_name+
* +:conditions+
-* +:counter_sql+
-* +:delete_sql+
* +:extend+
-* +:finder_sql+
* +:foreign_key+
* +:group+
* +:include+
-* +:insert_sql+
* +:join_table+
* +:limit+
* +:offset+
@@ -1767,24 +1751,10 @@ end
If you use a hash-style +:conditions+ option, then record creation via this association will be automatically scoped using the hash. In this case, using +@parts.assemblies.create+ or +@parts.assemblies.build+ will create orders where the +factory+ column has the value "Seattle".
-h6(#has_and_belongs_to_many-counter_sql). +:counter_sql+
-
-Normally Rails automatically generates the proper SQL to count the association members. With the +:counter_sql+ option, you can specify a complete SQL statement to count them yourself.
-
-NOTE: If you specify +:finder_sql+ but not +:counter_sql+, then the counter SQL will be generated by substituting the +SELECT ... FROM+ clause of your +:finder_sql+ statement by +SELECT COUNT(*) FROM+.
-
-h6(#has_and_belongs_to_many-delete_sql). +:delete_sql+
-
-Normally Rails automatically generates the proper SQL to remove links between the associated classes. With the +:delete_sql+ option, you can specify a complete SQL statement to delete them yourself.
-
h6(#has_and_belongs_to_many-extend). +:extend+
The +:extend+ option specifies a named module to extend the association proxy. Association extensions are discussed in detail <a href="#association-extensions">later in this guide</a>.
-h6(#has_and_belongs_to_many-finder_sql). +:finder_sql+
-
-Normally Rails automatically generates the proper SQL to fetch the association members. With the +:finder_sql+ option, you can specify a complete SQL statement to fetch them yourself. If fetching objects requires complex multi-table SQL, this may be necessary.
-
h6(#has_and_belongs_to_many-foreign_key). +:foreign_key+
By convention, Rails assumes that the column in the join table used to hold the foreign key pointing to this model is the name of this model with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly:
@@ -1811,10 +1781,6 @@ h6(#has_and_belongs_to_many-include). +:include+
You can use the +:include+ option to specify second-order associations that should be eager-loaded when this association is used.
-h6(#has_and_belongs_to_many-insert_sql). +:insert_sql+
-
-Normally Rails automatically generates the proper SQL to create links between the associated classes. With the +:insert_sql+ option, you can specify a complete SQL statement to insert them yourself.
-
h6(#has_and_belongs_to_many-join_table). +:join_table+
If the default name of the join table, based on lexical ordering, is not what you want, you can use the +:join_table+ option to override the default.
diff --git a/guides/source/configuring.textile b/guides/source/configuring.textile
index af46538bf5..cd9aab4892 100644
--- a/guides/source/configuring.textile
+++ b/guides/source/configuring.textile
@@ -424,7 +424,7 @@ There are a number of settings available on +config.action_mailer+:
* +config.action_mailer.perform_deliveries+ specifies whether mail will actually be delivered and is true by default. It can be convenient to set it to false for testing.
-* +config.action_mailer.default+ configures Action Mailer defaults. These default to:
+* +config.action_mailer.default_options+ configures Action Mailer defaults. Use to set options like `from` or `reply_to` for every mailer. These default to:
<ruby>
:mime_version => "1.0",
:charset => "UTF-8",
diff --git a/guides/source/contributing_to_ruby_on_rails.textile b/guides/source/contributing_to_ruby_on_rails.textile
index 1dadce2083..a8a097d156 100644
--- a/guides/source/contributing_to_ruby_on_rails.textile
+++ b/guides/source/contributing_to_ruby_on_rails.textile
@@ -34,6 +34,8 @@ h4. What about Feature Requests?
Please don't put "feature request" items into GitHub Issues. If there's a new feature that you want to see added to Ruby on Rails, you'll need to write the code yourself - or convince someone else to partner with you to write the code. Later in this guide you'll find detailed instructions for proposing a patch to Ruby on Rails. If you enter a wishlist item in GitHub Issues with no code, you can expect it to be marked "invalid" as soon as it's reviewed.
+If you'd like feedback on an idea for a feature before doing the work for make a patch, please send an email to the "rails-core mailing list":https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core. You might get no response, which means that everyone is indifferent. You might find someone who's also interested in building that feature. You might get a "This won't be accepted." But it's the proper place to discuss new ideas. GitHub Issues are not a particularly good venue for the sometimes long and involved discussions new features require.
+
h3. Running the Test Suite
To move on from submitting bugs to helping resolve existing issues or contributing your own code to Ruby on Rails, you _must_ be able to run its test suite. In this section of the guide you'll learn how to set up the tests on your own computer.
@@ -66,12 +68,26 @@ Install first libxml2 and libxslt together with their development files for Noko
$ sudo apt-get install libxml2 libxml2-dev libxslt1-dev
</shell>
+If you are on Fedora or CentOS, you can run
+
+<shell>
+$ sudo yum install libxml2 libxml2-devel libxslt libxslt-devel
+</shell>
+
+If you have any problems with these libraries, you should install them manually compiling the source code. Just follow the instructions "here":http://nokogiri.org/tutorials/installing_nokogiri.html#red_hat__centos .
+
Also, SQLite3 and its development files for the +sqlite3-ruby+ gem -- in Ubuntu you're done with just
<shell>
$ sudo apt-get install sqlite3 libsqlite3-dev
</shell>
+And if you are on Fedora or CentOS, you're done with
+
+<shell>
+$ sudo yum install sqlite3 sqlite3-devel
+</shell>
+
Get a recent version of "Bundler":http://gembundler.com/:
<shell>
@@ -150,6 +166,13 @@ $ sudo apt-get install mysql-server libmysqlclient15-dev
$ sudo apt-get install postgresql postgresql-client postgresql-contrib libpq-dev
</shell>
+On Fedora or CentOS, just run:
+
+<shell>
+$ sudo yum install mysql-server mysql-devel
+$ sudo yum install postgresql-server postgresql-devel
+</shell>
+
After that run:
<shell>
@@ -172,7 +195,7 @@ and create the test databases:
<shell>
$ cd activerecord
-$ rake mysql:build_databases
+$ bundle exec rake mysql:build_databases
</shell>
PostgreSQL's authentication works differently. A simple way to set up the development environment for example is to run with your development account
@@ -185,7 +208,7 @@ and then create the test databases with
<shell>
$ cd activerecord
-$ rake postgresql:build_databases
+$ bundle exec rake postgresql:build_databases
</shell>
NOTE: Using the rake task to create the test databases ensures they have the correct character set and collation.
diff --git a/guides/source/debugging_rails_applications.textile b/guides/source/debugging_rails_applications.textile
index cc172042e9..667f2d2140 100644
--- a/guides/source/debugging_rails_applications.textile
+++ b/guides/source/debugging_rails_applications.textile
@@ -626,7 +626,7 @@ In this section, you will learn how to find and fix such leaks by using tools su
h4. BleakHouse
-"BleakHouse":https://github.com/fauna/bleak_house/tree/master is a library for finding memory leaks.
+"BleakHouse":https://github.com/evan/bleak_house/ is a library for finding memory leaks.
If a Ruby object does not go out of scope, the Ruby Garbage Collector won't sweep it since it is referenced somewhere. Leaks like this can grow slowly and your application will consume more and more memory, gradually affecting the overall system performance. This tool will help you find leaks on the Ruby heap.
@@ -675,7 +675,7 @@ To analyze it, just run the listed command. The top 20 leakiest lines will be li
This way you can find where your application is leaking memory and fix it.
-If "BleakHouse":https://github.com/fauna/bleak_house/tree/master doesn't report any heap growth but you still have memory growth, you might have a broken C extension, or real leak in the interpreter. In that case, try using Valgrind to investigate further.
+If "BleakHouse":https://github.com/evan/bleak_house/ doesn't report any heap growth but you still have memory growth, you might have a broken C extension, or real leak in the interpreter. In that case, try using Valgrind to investigate further.
h4. Valgrind
@@ -708,4 +708,4 @@ h3. References
* "Debugging with ruby-debug":http://bashdb.sourceforge.net/ruby-debug.html
* "ruby-debug cheat sheet":http://cheat.errtheblog.com/s/rdebug/
* "Ruby on Rails Wiki: How to Configure Logging":http://wiki.rubyonrails.org/rails/pages/HowtoConfigureLogging
-* "Bleak House Documentation":http://blog.evanweaver.com/files/doc/fauna/bleak_house/files/README.html
+* "Bleak House Documentation":http://blog.evanweaver.com/files/doc/fauna/bleak_house/
diff --git a/guides/source/engines.textile b/guides/source/engines.textile
index 86e7254201..53c2845731 100644
--- a/guides/source/engines.textile
+++ b/guides/source/engines.textile
@@ -347,7 +347,7 @@ The form will be making a +POST+ request to +/posts/:post_id/comments+, which wi
<ruby>
def create
@post = Post.find(params[:post_id])
- @comment = @post.comments.build(params[:comment])
+ @comment = @post.comments.create(params[:comment])
flash[:notice] = "Comment has been created!"
redirect_to post_path
end
@@ -563,7 +563,7 @@ end
By default, the engine's controllers inherit from <tt>Blorgh::ApplicationController</tt>. So, after making this change they will have access to the main applications +ApplicationController+ as though they were part of the main application.
-This change does require that the engine is run from a Rails application that has an +ApplicationController+.
+This change does require that the engine is run from a Rails application that has an +ApplicationController+.
h4. Configuring an engine
@@ -734,12 +734,14 @@ You can also specify these assets as dependencies of other assets using the Asse
*/
</plain>
+INFO. Remember that in order to use languages like Sass or CoffeeScript, you should add the relevant library to your engine's +.gemspec+.
+
h4. Separate Assets & Precompiling
There are some situations where your engine's assets not required by the host application. For example, say that you've created
an admin functionality that only exists for your engine. In this case, the host application doesn't need to require +admin.css+
or +admin.js+. Only the gem's admin layout needs these assets. It doesn't make sense for the host app to include +"blorg/admin.css"+ in it's stylesheets. In this situation, you should explicitly define these assets for precompilation.
-This tells sprockets to add you engine assets when +rake assets:precompile+ is ran.
+This tells sprockets to add you engine assets when +rake assets:precompile+ is ran.
You can define assets for precompilation in +engine.rb+
@@ -753,18 +755,40 @@ For more information, read the "Asset Pipeline guide":http://guides.rubyonrails.
h4. Other gem dependencies
-Gem dependencies inside an engine should be specified inside the +.gemspec+ file that's at the root of the engine. The reason for this is because the engine may be installed as a gem. If dependencies were to be specified inside the +Gemfile+, these would not be recognised by a traditional gem install and so they would not be installed, causing the engine to malfunction.
+Gem dependencies inside an engine should be specified inside the +.gemspec+ file
+that's at the root of the engine. The reason for this is because the engine may
+be installed as a gem. If dependencies were to be specified inside the +Gemfile+,
+these would not be recognised by a traditional gem install and so they would not
+be installed, causing the engine to malfunction.
-To specify a dependency that should be installed with the engine during a traditional +gem install+, specify it inside the +Gem::Specification+ block inside the +.gemspec+ file in the engine:
+To specify a dependency that should be installed with the engine during a
+traditional +gem install+, specify it inside the +Gem::Specification+ block
+inside the +.gemspec+ file in the engine:
<ruby>
s.add_dependency "moo"
</ruby>
-To specify a dependency that should only be installed as a development dependency of the application, specify it like this:
+To specify a dependency that should only be installed as a development
+dependency of the application, specify it like this:
<ruby>
s.add_development_dependency "moo"
</ruby>
-Both kinds of dependencies will be installed when +bundle install+ is run inside the application. The development dependencies for the gem will only be used when the tests for the engine are running.
+Both kinds of dependencies will be installed when +bundle install+ is run inside
+the application. The development dependencies for the gem will only be used when
+the tests for the engine are running.
+
+Note that if you want to immediately require dependencies when the engine is
+required, you should require them before engine's initialization. For example:
+
+<ruby>
+require 'other_engine/engine'
+require 'yet_another_engine/engine'
+
+module MyEngine
+ class Engine < ::Rails::Engine
+ end
+end
+</ruby> \ No newline at end of file
diff --git a/guides/source/form_helpers.textile b/guides/source/form_helpers.textile
index 1851aceff8..58338ce54b 100644
--- a/guides/source/form_helpers.textile
+++ b/guides/source/form_helpers.textile
@@ -10,7 +10,7 @@ In this guide you will:
* Understand the date and time helpers Rails provides
* Learn what makes a file upload form different
* Learn some cases of building forms to external resources
-* Find out where to look for complex forms
+* Find out how to build complex forms
endprologue.
@@ -816,11 +816,130 @@ Or if you don't want to render an +authenticity_token+ field:
h3. Building Complex Forms
-Many apps grow beyond simple forms editing a single object. For example when creating a Person you might want to allow the user to (on the same form) create multiple address records (home, work, etc.). When later editing that person the user should be able to add, remove or amend addresses as necessary. While this guide has shown you all the pieces necessary to handle this, Rails does not yet have a standard end-to-end way of accomplishing this, but many have come up with viable approaches. These include:
+Many apps grow beyond simple forms editing a single object. For example when creating a Person you might want to allow the user to (on the same form) create multiple address records (home, work, etc.). When later editing that person the user should be able to add, remove or amend addresses as necessary.
-* As of Rails 2.3, Rails includes "Nested Attributes":./2_3_release_notes.html#nested-attributes and "Nested Object Forms":./2_3_release_notes.html#nested-object-forms
-* Ryan Bates' series of Railscasts on "complex forms":http://railscasts.com/episodes/75
-* Handle Multiple Models in One Form from "Advanced Rails Recipes":http://media.pragprog.com/titles/fr_arr/multiple_models_one_form.pdf
-* Eloy Duran's "complex-forms-examples":https://github.com/alloy/complex-form-examples/ application
-* Lance Ivy's "nested_assignment":https://github.com/cainlevy/nested_assignment/tree/master plugin and "sample application":https://github.com/cainlevy/complex-form-examples/tree/cainlevy
-* James Golick's "attribute_fu":https://github.com/jamesgolick/attribute_fu plugin
+h4. Configuring the Model
+
+Active Record provides model level support via the +accepts_nested_attributes_for+ method:
+
+<ruby>
+class Person < ActiveRecord::Base
+ has_many :addresses
+ accepts_nested_attributes_for :addresses
+
+ attr_accessible :name, :addresses_attributes
+end
+
+class Address < ActiveRecord::Base
+ belongs_to :person
+ attr_accessible :kind, :street
+end
+</ruby>
+
+This creates an +addresses_attributes=+ method on +Person+ that allows you to create, update and (optionally) destroy addresses. When using +attr_accessible+ or +attr_protected+ you must mark +addresses_attributes+ as accessible as well as the other attributes of +Person+ and +Address+ that should be mass assigned.
+
+h4. Building the Form
+
+The following form allows a user to create a +Person+ and its associated addresses.
+
+<erb>
+<%= form_for @person do |f| %>
+ Addresses:
+ <ul>
+ <%= f.fields_for :addresses do |addresses_form| %>
+ <li>
+ <%= addresses_form.label :kind %>
+ <%= addresses_form.text_field :kind %>
+
+ <%= addresses_form.label :street %>
+ <%= addresses_form.text_field :street %>
+ ...
+ </li>
+ <% end %>
+ </ul>
+<% end %>
+</erb>
+
+
+When an association accepts nested attributes +fields_for+ renders its block once for every element of the association. In particular, if a person has no addresses it renders nothing. A common pattern is for the controller to build one or more empty children so that at least one set of fields is shown to the user. The example below would result in 3 sets of address fields being rendered on the new person form.
+
+<ruby>
+def new
+ @person = Person.new
+ 3.times { @person.addresses.build}
+end
+</ruby>
+
++fields_for+ yields a form builder that names parameters in the format expected the accessor generated by +accepts_nested_attributes_for+. For example when creating a user with 2 addresses, the submitted parameters would look like
+
+<ruby>
+{
+ :person => {
+ :name => 'John Doe',
+ :addresses_attributes => {
+ '0' => {
+ :kind => 'Home',
+ :street => '221b Baker Street',
+ },
+ '1' => {
+ :kind => 'Office',
+ :street => '31 Spooner Street'
+ }
+ }
+ }
+}
+</ruby>
+
+The keys of the +:addresses_attributes+ hash are unimportant, they need merely be different for each address.
+
+If the associated object is already saved, +fields_for+ autogenerates a hidden input with the +id+ of the saved record. You can disable this by passing +:include_id => false+ to +fields_for+. You may wish to do this if the autogenerated input is placed in a location where an input tag is not valid HTML or when using an ORM where children do not have an id.
+
+h4. The Controller
+
+You do not need to write any specific controller code to use nested attributes. Create and update records as you would with a simple form.
+
+h4. Removing Objects
+
+You can allow users to delete associated objects by passing +allow_destroy => true+ to +accepts_nested_attributes_for+
+
+<ruby>
+class Person < ActiveRecord::Base
+ has_many :addresses
+ accepts_nested_attributes_for :addresses, :allow_destroy => true
+end
+</ruby>
+
+If the hash of attributes for an object contains the key +_destroy+ with a value of '1' or 'true' then the object will be destroyed. This form allows users to remove addresses:
+
+<erb>
+<%= form_for @person do |f| %>
+ Addresses:
+ <ul>
+ <%= f.fields_for :addresses do |addresses_form| %>
+ <li>
+ <%= check_box :_destroy%>
+ <%= addresses_form.label :kind %>
+ <%= addresses_form.text_field :kind %>
+ ...
+ </li>
+ <% end %>
+ </ul>
+<% end %>
+</erb>
+
+h4. Preventing Empty Records
+
+It is often useful to ignore sets of fields that the user has not filled in. You can control this by passing a +:reject_if+ proc to +accepts_nested_attributes_for+. This proc will be called with each hash of attributes submitted by the form. If the proc returns +false+ then Active Record will not build an associated object for that hash. The example below only tries to build an address if the +kind+ attribute is set.
+
+<ruby>
+class Person < ActiveRecord::Base
+ has_many :addresses
+ accepts_nested_attributes_for :addresses, :reject_if => lambda {|attributes| attributes['kind'].blank?}
+end
+</ruby>
+
+As a convenience you can instead pass the symbol +:all_blank+ which will create a proc that will reject records where all the attributes are blank excluding any value for +_destroy+.
+
+h4. Adding Fields on the Fly
+
+Rather than rendering multiple sets of fields ahead of time you may wish to add them only when a user clicks on an 'Add new child' button. Rails does not provide any builtin support for this. When generating new sets of fields you must ensure the the key of the associated array is unique - the current javascript date (milliseconds after the epoch) is a common choice. \ No newline at end of file
diff --git a/guides/source/getting_started.textile b/guides/source/getting_started.textile
index 07419d11b4..8d7c0d4bea 100644
--- a/guides/source/getting_started.textile
+++ b/guides/source/getting_started.textile
@@ -1144,7 +1144,7 @@ together.
<td><%= post.text %></td>
<td><%= link_to 'Show', :action => :show, :id => post.id %></td>
<td><%= link_to 'Edit', :action => :edit, :id => post.id %></td>
- <td><%= link_to 'Destroy', { :action => :destroy, :id => post.id }, :method => :delete, :confirm => 'Are you sure?' %></td>
+ <td><%= link_to 'Destroy', { :action => :destroy, :id => post.id }, :method => :delete, :data => { :confirm => 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
@@ -1152,13 +1152,12 @@ together.
Here we're using +link_to+ in a different way. We wrap the
+:action+ and +:id+ attributes in a hash so that we can pass those two keys in
-first as one argument, and then the final two keys as another argument. The +:method+ and +:confirm+
+first as one argument, and then the final two keys as another argument. The +:method+ and +:'data-confirm'+
options are used as HTML5 attributes so that when the link is clicked,
-Rails will first show a confirm dialog to the user, and then submit the
-link with method +delete+. This is done via the JavaScript file +jquery_ujs+
-which is automatically included into your application's layout
-(+app/views/layouts/application.html.erb+) when you generated the application.
-Without this file, the confirmation dialog box wouldn't appear.
+Rails will first show a confirm dialog to the user, and then submit the link with method +delete+.
+This is done via the JavaScript file +jquery_ujs+ which is automatically included
+into your application's layout (+app/views/layouts/application.html.erb+) when you
+generated the application. Without this file, the confirmation dialog box wouldn't appear.
!images/getting_started/confirm_dialog.png(Confirm Dialog)!
@@ -1248,6 +1247,7 @@ First, take a look at +comment.rb+:
<ruby>
class Comment < ActiveRecord::Base
belongs_to :post
+ attr_accessible :body, :commenter
end
</ruby>
@@ -1626,8 +1626,8 @@ So first, let's add the delete link in the
<p>
<%= link_to 'Destroy Comment', [comment.post, comment],
- :confirm => 'Are you sure?',
- :method => :delete %>
+ :method => :delete,
+ :data => { :confirm => 'Are you sure?' } %>
</p>
</erb>
diff --git a/guides/source/i18n.textile b/guides/source/i18n.textile
index ee7176a6c8..8ad6ee4b73 100644
--- a/guides/source/i18n.textile
+++ b/guides/source/i18n.textile
@@ -220,7 +220,7 @@ Every helper method dependent on +url_for+ (e.g. helpers for named routes like +
You may be satisfied with this. It does impact the readability of URLs, though, when the locale "hangs" at the end of every URL in your application. Moreover, from the architectural standpoint, locale is usually hierarchically above the other parts of the application domain: and URLs should reflect this.
-You probably want URLs to look like this: +www.example.com/en/books+ (which loads the English locale) and +www.example.com/nl/books+ (which loads the Dutch locale). This is achievable with the "over-riding +default_url_options+" strategy from above: you just have to set up your routes with "+path_prefix+":http://api.rubyonrails.org/classes/ActionController/Resources.html#M000354 option in this way:
+You probably want URLs to look like this: +www.example.com/en/books+ (which loads the English locale) and +www.example.com/nl/books+ (which loads the Dutch locale). This is achievable with the "over-riding +default_url_options+" strategy from above: you just have to set up your routes with "+scoping+":http://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html option in this way:
<ruby>
# config/routes.rb
diff --git a/guides/source/layouts_and_rendering.textile b/guides/source/layouts_and_rendering.textile
index 55bd521419..32ceecea18 100644
--- a/guides/source/layouts_and_rendering.textile
+++ b/guides/source/layouts_and_rendering.textile
@@ -78,7 +78,7 @@ If we want to display the properties of all the books in our view, we can do so
<td><%= book.content %></td>
<td><%= link_to "Show", book %></td>
<td><%= link_to "Edit", edit_book_path(book) %></td>
- <td><%= link_to "Remove", book, :confirm => "Are you sure?", :method => :delete %></td>
+ <td><%= link_to "Remove", book, :method => :delete, :data => { :confirm => "Are you sure?" } %></td>
</tr>
<% end %>
</table>
diff --git a/guides/source/migrations.textile b/guides/source/migrations.textile
index 342b5a4d57..06e85e5914 100644
--- a/guides/source/migrations.textile
+++ b/guides/source/migrations.textile
@@ -111,6 +111,7 @@ Active Record provides methods that perform common data definition tasks in a
database independent way (you'll read about them in detail later):
* +add_column+
+* +add_reference+
* +add_index+
* +change_column+
* +change_table+
@@ -120,6 +121,7 @@ database independent way (you'll read about them in detail later):
* +remove_column+
* +remove_index+
* +rename_column+
+* +remove_reference+
If you need to perform tasks specific to your database (for example create a
"foreign key":#active-record-and-referential-integrity constraint) then the
@@ -332,6 +334,51 @@ NOTE: The generated migration file for destructive migrations will still be
old-style using the +up+ and +down+ methods. This is because Rails needs to know
the original data types defined when you made the original changes.
+Also the generator accepts column type as +references+(also available as +belongs_to+), for instance
+
+<shell>
+$ rails generate migration AddUserRefToProducts user:references
+</shell>
+
+generates
+
+<ruby>
+class AddUserRefToProducts < ActiveRecord::Migration
+ def change
+ add_reference :products, :user, :index => true
+ end
+end
+</ruby>
+
+This migration will create a user_id column and appropriate index.
+
+h4. Supported type modifiers
+
+You can also specify some options just after the field type between curly braces. You can use the
+following modifiers:
+
+* +limit+ Sets the maximum size of the +string/text/binary/integer+ fields
+* +precision+ Defines the precision for the +decimal+ fields
+* +scale+ Defines the scale for the +decimal+ fields
+* +polymorphic+ Adds a +type+ column for +belongs_to+ associations
+
+For instance running
+
+<shell>
+$ rails generate migration AddDetailsToProducts price:decimal{5,2} supplier:references{polymorphic}
+</shell>
+
+will produce a migration that looks like this
+
+<ruby>
+class AddDetailsToProducts < ActiveRecord::Migration
+ def change
+ add_column :products, :price, :precision => 5, :scale => 2
+ add_reference :products, :user, :polymorphic => true, :index => true
+ end
+end
+</ruby>
+
h3. Writing a Migration
Once you have created your migration using one of the generators it's time to
diff --git a/guides/source/plugins.textile b/guides/source/plugins.textile
index 95e38db483..9001857a5f 100644
--- a/guides/source/plugins.textile
+++ b/guides/source/plugins.textile
@@ -427,4 +427,4 @@ h4. References
* "Developing a RubyGem using Bundler":https://github.com/radar/guides/blob/master/gem-development.md
* "Using Gemspecs As Intended":http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended/
* "Gemspec Reference":http://docs.rubygems.org/read/chapter/20
-* "GemPlugins":http://www.mbleigh.com/2008/06/11/gemplugins-a-brief-introduction-to-the-future-of-rails-plugins
+* "GemPlugins":http://www.intridea.com/blog/2008/6/11/gemplugins-a-brief-introduction-to-the-future-of-rails-plugins
diff --git a/guides/source/security.textile b/guides/source/security.textile
index 626d6fa508..8879122b66 100644
--- a/guides/source/security.textile
+++ b/guides/source/security.textile
@@ -851,7 +851,7 @@ Network traffic is mostly based on the limited Western alphabet, so new characte
&amp;#108;&amp;#101;&amp;#114;&amp;#116;&amp;#40;&amp;#39;&amp;#88;&amp;#83;&amp;#83;&amp;#39;&amp;#41;>
</html>
-This example pops up a message box. It will be recognized by the above sanitize() filter, though. A great tool to obfuscate and encode strings, and thus “get to know your enemy”, is the "Hackvertor":http://www.businessinfo.co.uk/labs/hackvertor/hackvertor.php. Rails' sanitize() method does a good job to fend off encoding attacks.
+This example pops up a message box. It will be recognized by the above sanitize() filter, though. A great tool to obfuscate and encode strings, and thus “get to know your enemy”, is the "Hackvertor":https://hackvertor.co.uk/public. Rails' sanitize() method does a good job to fend off encoding attacks.
h5. Examples from the Underground
diff --git a/guides/source/testing.textile b/guides/source/testing.textile
index d35be6a70e..4faf59fad8 100644
--- a/guides/source/testing.textile
+++ b/guides/source/testing.textile
@@ -1,62 +1,57 @@
h2. A Guide to Testing Rails Applications
-This guide covers built-in mechanisms offered by Rails to test your application. By referring to this guide, you will be able to:
+This guide covers built-in mechanisms offered by Rails to test your
+application. By referring to this guide, you will be able to:
* Understand Rails testing terminology
-* Write unit, functional and integration tests for your application
+* Write unit, functional, and integration tests for your application
* Identify other popular testing approaches and plugins
-This guide won't teach you to write a Rails application; it assumes basic familiarity with the Rails way of doing things.
-
endprologue.
h3. Why Write Tests for your Rails Applications?
-* Rails makes it super easy to write your tests. It starts by producing skeleton test code in the background while you are creating your models and controllers.
-* By simply running your Rails tests you can ensure your code adheres to the desired functionality even after some major code refactoring.
-* Rails tests can also simulate browser requests and thus you can test your application's response without having to test it through your browser.
-
-h3. Introduction to Testing
-
-Testing support was woven into the Rails fabric from the beginning. It wasn't an "oh! let's bolt on support for running tests because they're new and cool" epiphany. Just about every Rails application interacts heavily with a database - and, as a result, your tests will need a database to interact with as well. To write efficient tests, you'll need to understand how to set up this database and populate it with sample data.
+Rails makes it super easy to write your tests. It starts by producing skeleton test code while you are creating your models and controllers.
-h4. The Three Environments
+By simply running your Rails tests you can ensure your code adheres to the desired functionality even after some major code refactoring.
-Every Rails application you build has 3 sides: a side for production, a side for development, and a side for testing.
+Rails tests can also simulate browser requests and thus you can test your application's response without having to test it through your browser.
-One place you'll find this distinction is in the +config/database.yml+ file. This YAML configuration file has 3 different sections defining 3 unique database setups:
+h3. Introduction to Testing
-* production
-* development
-* test
+Testing support was woven into the Rails fabric from the beginning. It wasn't an "oh! let's bolt on support for running tests because they're new and cool" epiphany. Just about every Rails application interacts heavily with a database and, as a result, your tests will need a database to interact with as well. To write efficient tests, you'll need to understand how to set up this database and populate it with sample data.
-This allows you to set up and interact with test data without any danger of your tests altering data from your production environment.
+h4. The Test Environment
-For example, suppose you need to test your new +delete_this_user_and_every_everything_associated_with_it+ function. Wouldn't you want to run this in an environment where it makes no difference if you destroy data or not?
+By default, every Rails application has three environments: development, test, and production. The database for each one of them is configured in +config/database.yml+.
-When you do end up destroying your testing database (and it will happen, trust me), you can rebuild it from scratch according to the specs defined in the development database. You can do this by running +rake db:test:prepare+.
+A dedicated test database allows you to set up and interact with test data in isolation. Tests can mangle test data with confidence, that won't touch the data in the development or production databases.
h4. Rails Sets up for Testing from the Word Go
Rails creates a +test+ folder for you as soon as you create a Rails project using +rails new+ _application_name_. If you list the contents of this folder then you shall see:
<shell>
-$ ls -F test/
+$ ls -F test
-fixtures/ functional/ integration/ test_helper.rb unit/
+fixtures/ functional/ integration/ performance/ test_helper.rb unit/
</shell>
-The +unit+ folder is meant to hold tests for your models, the +functional+ folder is meant to hold tests for your controllers, and the +integration+ folder is meant to hold tests that involve any number of controllers interacting. Fixtures are a way of organizing test data; they reside in the +fixtures+ folder. The +test_helper.rb+ file holds the default configuration for your tests.
+The +unit+ directory is meant to hold tests for your models, the +functional+ directory is meant to hold tests for your controllers, the +integration+ directory is meant to hold tests that involve any number of controllers interacting, and the +performance+ directory is meant for performance tests.
+
+Fixtures are a way of organizing test data; they reside in the +fixtures+ folder.
+
+The +test_helper.rb+ file holds the default configuration for your tests.
h4. The Low-Down on Fixtures
For good tests, you'll need to give some thought to setting up test data. In Rails, you can handle this by defining and customizing fixtures.
-h5. What are Fixtures?
+h5. What Are Fixtures?
-_Fixtures_ is a fancy word for sample data. Fixtures allow you to populate your testing database with predefined data before your tests run. Fixtures are database independent and assume a single format: *YAML*.
+_Fixtures_ is a fancy word for sample data. Fixtures allow you to populate your testing database with predefined data before your tests run. Fixtures are database independent written in YAML. There is one file per model.
-You'll find fixtures under your +test/fixtures+ directory. When you run +rails generate model+ to create a new model, fixture stubs will be automatically created and placed in this directory.
+You'll find fixtures under your +test/fixtures+ directory. When you run +rails generate model+ to create a new model fixture stubs will be automatically created and placed in this directory.
h5. YAML
@@ -77,35 +72,20 @@ steve:
profession: guy with keyboard
</yaml>
-Each fixture is given a name followed by an indented list of colon-separated key/value pairs. Records are separated by a blank space. You can place comments in a fixture file by using the # character in the first column.
+Each fixture is given a name followed by an indented list of colon-separated key/value pairs. Records are typically separated by a blank space. You can place comments in a fixture file by using the # character in the first column.
h5. ERB'in It Up
-ERB allows you to embed ruby code within templates. YAML fixture format is pre-processed with ERB when you load fixtures. This allows you to use Ruby to help you generate some sample data.
-
-<erb>
-<% earth_size = 20 %>
-mercury:
- size: <%= earth_size / 50 %>
- brightest_on: <%= 113.days.ago.to_s(:db) %>
-
-venus:
- size: <%= earth_size / 2 %>
- brightest_on: <%= 67.days.ago.to_s(:db) %>
-
-mars:
- size: <%= earth_size - 69 %>
- brightest_on: <%= 13.days.from_now.to_s(:db) %>
-</erb>
-
-Anything encased within the
+ERB allows you to embed Ruby code within templates. The YAML fixture format is pre-processed with ERB when Rails loads fixtures. This allows you to use Ruby to help you generate some sample data. For example, the following code generates a thousand users:
<erb>
-<% %>
+<% 1000.times do |n| %>
+user_<%= n %>:
+ username: <%= "user%03d" % n %>
+ email: <%= "user%03d@example.com" % n %>
+<% end %>
</erb>
-tag is considered Ruby code. When this fixture is loaded, the +size+ attribute of the three records will be set to 20/50, 20/2, and 20-69 respectively. The +brightest_on+ attribute will also be evaluated and formatted by Rails to be compatible with the database.
-
h5. Fixtures in Action
Rails by default automatically loads all fixtures from the +test/fixtures+ folder for your unit and functional test. Loading involves three steps:
@@ -377,12 +357,12 @@ There are a bunch of different types of assertions you can use. Here's the compl
|_.Assertion |_.Purpose|
|+assert( boolean, [msg] )+ |Ensures that the object/expression is true.|
-|+assert_equal( obj1, obj2, [msg] )+ |Ensures that +obj1 == obj2+ is true.|
-|+assert_not_equal( obj1, obj2, [msg] )+ |Ensures that +obj1 == obj2+ is false.|
-|+assert_same( obj1, obj2, [msg] )+ |Ensures that +obj1.equal?(obj2)+ is true.|
-|+assert_not_same( obj1, obj2, [msg] )+ |Ensures that +obj1.equal?(obj2)+ is false.|
+|+assert_equal( expected, actual, [msg] )+ |Ensures that +expected == actual+ is true.|
+|+assert_not_equal( expected, actual, [msg] )+ |Ensures that +expected != actual+ is true.|
+|+assert_same( expected, actual, [msg] )+ |Ensures that +expected.equal?(actual)+ is true.|
+|+assert_not_same( expected, actual, [msg] )+ |Ensures that +!expected.equal?(actual)+ is true.|
|+assert_nil( obj, [msg] )+ |Ensures that +obj.nil?+ is true.|
-|+assert_not_nil( obj, [msg] )+ |Ensures that +obj.nil?+ is false.|
+|+assert_not_nil( obj, [msg] )+ |Ensures that +!obj.nil?+ is true.|
|+assert_match( regexp, string, [msg] )+ |Ensures that a string matches the regular expression.|
|+assert_no_match( regexp, string, [msg] )+ |Ensures that a string doesn't match the regular expression.|
|+assert_in_delta( expecting, actual, delta, [msg] )+ |Ensures that the numbers +expecting+ and +actual+ are within +delta+ of each other.|
@@ -526,7 +506,7 @@ You also have access to three instance variables in your functional tests:
h4. Testing Templates and Layouts
-If you want to make sure that the response rendered the correct template and layout, you can use the +assert_template+
+If you want to make sure that the response rendered the correct template and layout, you can use the +assert_template+
method:
<ruby>
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 7223270210..5165d9401f 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,5 +1,16 @@
## Rails 4.0.0 (unreleased) ##
+* The migration generator will now produce AddXXXToYYY/RemoveXXXFromYYY migrations with references statements, for instance
+
+ rails g migration AddReferencesToProducts user:references supplier:references{polymorphic}
+
+ will generate the migration with:
+
+ add_reference :products, :user, index: true
+ add_reference :products, :supplier, polymorphic: true, index: true
+
+ *Aleksey Magusev*
+
* Allow scaffold/model/migration generators to accept a `polymorphic` modifier
for `references`/`belongs_to`, for instance
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index d5ec2cbfd9..048219002d 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -177,7 +177,7 @@ module Rails
end
def queue #:nodoc:
- @queue ||= build_queue
+ @queue ||= Queueing::Container.new(build_queue)
end
def build_queue #:nodoc:
@@ -267,6 +267,7 @@ module Rails
def default_middleware_stack #:nodoc:
ActionDispatch::MiddlewareStack.new.tap do |middleware|
+ app = self
if rack_cache = config.action_controller.perform_caching && config.action_dispatch.rack_cache
require "action_dispatch/http/rack_cache"
middleware.use ::Rack::Cache, rack_cache
@@ -290,11 +291,10 @@ module Rails
middleware.use ::ActionDispatch::RequestId
middleware.use ::Rails::Rack::Logger, config.log_tags # must come after Rack::MethodOverride to properly log overridden methods
middleware.use ::ActionDispatch::ShowExceptions, config.exceptions_app || ActionDispatch::PublicExceptions.new(Rails.public_path)
- middleware.use ::ActionDispatch::DebugExceptions
+ middleware.use ::ActionDispatch::DebugExceptions, app
middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies
unless config.cache_classes
- app = self
middleware.use ::ActionDispatch::Reloader, lambda { app.reload_dependencies? }
end
@@ -310,7 +310,7 @@ module Rails
end
middleware.use ::ActionDispatch::ParamsParser
- middleware.use ::ActionDispatch::Head
+ middleware.use ::Rack::Head
middleware.use ::Rack::ConditionalGet
middleware.use ::Rack::ETag, "no-cache"
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 383c159d3d..f469c334a7 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -176,8 +176,7 @@ module Rails
#
# * routes: when you mount an Engine with <tt>mount(MyEngine::Engine => '/my_engine')</tt>,
# it's used as default :as option
- # * some of the rake tasks are based on engine name, e.g. <tt>my_engine:install:migrations</tt>,
- # <tt>my_engine:install:assets</tt>
+ # * rake task for installing migrations <tt>my_engine:install:migrations</tt>
#
# Engine name is set by default based on class name. For <tt>MyEngine::Engine</tt> it will be
# <tt>my_engine_engine</tt>. You can change it manually using the <tt>engine_name</tt> method:
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
index d78d97b2b4..f5182bcc50 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
+++ b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
@@ -19,7 +19,7 @@
<% end -%>
<td><%%= link_to 'Show', <%= singular_table_name %> %></td>
<td><%%= link_to 'Edit', edit_<%= singular_table_name %>_path(<%= singular_table_name %>) %></td>
- <td><%%= link_to 'Destroy', <%= singular_table_name %>, confirm: 'Are you sure?', method: :delete %></td>
+ <td><%%= link_to 'Destroy', <%= singular_table_name %>, method: :delete, data: { confirm: 'Are you sure?' } %></td>
<%% end %>
</tbody>
</table>
diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb
index d480fc12b5..d2c2abf40c 100644
--- a/railties/lib/rails/generators/generated_attribute.rb
+++ b/railties/lib/rails/generators/generated_attribute.rb
@@ -8,6 +8,7 @@ module Rails
attr_accessor :name, :type
attr_reader :attr_options
+ attr_writer :index_name
class << self
def parse(column_definition)
@@ -89,18 +90,26 @@ module Rails
end
end
+ def plural_name
+ name.sub(/_id$/, '').pluralize
+ end
+
def human_name
- name.to_s.humanize
+ name.humanize
end
def index_name
- if reference?
+ @index_name ||= if reference?
polymorphic? ? %w(id type).map { |t| "#{name}_#{t}" } : "#{name}_id"
else
name
end
end
+ def foreign_key?
+ !!(name =~ /_id$/)
+ end
+
def reference?
self.class.reference?(type)
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
index 01f9396403..fee9eb9456 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
@@ -35,7 +35,7 @@
# Do not compress assets.
config.assets.compress = false
- # Expands the lines which load the assets.
+ # Debug mode disables concatenation and preprocessing of assets.
config.assets.debug = true
<%- end -%>
diff --git a/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb b/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb
index 9342a57b20..d09ce5ad34 100644
--- a/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb
+++ b/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb
@@ -6,7 +6,7 @@ class BrowsingTest < ActionDispatch::PerformanceTest
# self.profile_options = { runs: 5, metrics: [:wall_time, :memory],
# output: 'tmp/performance', formats: [:flat] }
- def test_homepage
+ test "homepage" do
get '/'
end
end
diff --git a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb
index 0090293200..9afda2d0df 100644
--- a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb
+++ b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb
@@ -6,7 +6,7 @@ class ActiveSupport::TestCase
<% unless options[:skip_active_record] -%>
ActiveRecord::Migration.check_pending!
- # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
+ # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
#
# Note: You'll currently still have to declare fixtures explicitly in integration tests
# -- they do not yet inherit this setting
diff --git a/railties/lib/rails/generators/rails/model/USAGE b/railties/lib/rails/generators/rails/model/USAGE
index 67f76aad01..c46c86076e 100644
--- a/railties/lib/rails/generators/rails/model/USAGE
+++ b/railties/lib/rails/generators/rails/model/USAGE
@@ -19,6 +19,55 @@ Description:
then the generator will create a module with a table_name_prefix method
to prefix the model's table name with the module name (e.g. admin_account)
+Available field types:
+
+ Just after the field name you can specify a type like text or boolean.
+ It will generate the column with the associated SQL type. For instance:
+
+ `rails generate model post title:string body:text`
+
+ will generate a title column with a varchar type and a body column with a text
+ type. You can use the following types:
+
+ integer
+ primary_key
+ decimal
+ float
+ boolean
+ binary
+ string
+ text
+ date
+ time
+ datetime
+ timestamp
+
+ You can also consider `references` as a kind of type. For instance, if you run:
+
+ `rails generate model photo title:string album:references`
+
+ It will generate an album_id column. You should generate this kind of fields when
+ you will use a `belongs_to` association for instance. `references` also support
+ the polymorphism, you could enable the polymorphism like this:
+
+ `rails generate model product supplier:references{polymorphic}`
+
+ You can also specify some options just after the field type. You can use the
+ following options:
+
+ limit Set the maximum size of the field giving a number between curly braces
+ default Set a default value for the field
+ precision Defines the precision for the decimal fields
+ scale Defines the scale for the decimal fields
+ uniq Defines the field values as unique
+ index Will add an index on the field
+
+ Examples:
+
+ `rails generate model user pseudo:string{30}`
+ `rails generate model user pseudo:string:uniq`
+
+
Examples:
`rails generate model account`
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/gitignore b/railties/lib/rails/generators/rails/plugin_new/templates/gitignore
index 458b2c662e..086d87818a 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/gitignore
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/gitignore
@@ -1,8 +1,10 @@
.bundle/
log/*.log
pkg/
+<% unless options[:skip_test_unit] && options[:dummy_path] == 'test/dummy' -%>
<%= dummy_path %>/db/*.sqlite3
<%= dummy_path %>/db/*.sqlite3-journal
<%= dummy_path %>/log/*.log
<%= dummy_path %>/tmp/
<%= dummy_path %>/.sass-cache
+<% end -%> \ No newline at end of file
diff --git a/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb b/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb
index 370750a175..2f25dcf832 100644
--- a/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb
+++ b/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb
@@ -6,7 +6,7 @@ class <%= class_name %>Test < ActionDispatch::PerformanceTest
# self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory],
# :output => 'tmp/performance', :formats => [:flat] }
- def test_homepage
+ test "homepage" do
get '/'
end
end
diff --git a/railties/lib/rails/info_controller.rb b/railties/lib/rails/info_controller.rb
index bacdcbf3aa..512803aeac 100644
--- a/railties/lib/rails/info_controller.rb
+++ b/railties/lib/rails/info_controller.rb
@@ -1,4 +1,4 @@
-require 'rails/application/routes_inspector'
+require 'action_dispatch/routing/inspector'
class Rails::InfoController < ActionController::Base
self.view_paths = File.join(File.dirname(__FILE__), 'templates')
@@ -15,7 +15,7 @@ class Rails::InfoController < ActionController::Base
end
def routes
- inspector = Rails::Application::RoutesInspector.new
+ inspector = ActionDispatch::Routing::RoutesInspector.new
@info = inspector.format(_routes.routes).join("\n")
end
diff --git a/railties/lib/rails/queueing.rb b/railties/lib/rails/queueing.rb
index b4bc7fcd18..baf6811d3e 100644
--- a/railties/lib/rails/queueing.rb
+++ b/railties/lib/rails/queueing.rb
@@ -1,7 +1,34 @@
require "thread"
+require 'delegate'
module Rails
module Queueing
+ # A container for multiple queues. This class delegates to a default Queue
+ # so that <tt>Rails.queue.push</tt> and friends will Just Work. To use this class
+ # with multiple queues:
+ #
+ # # In your configuration:
+ # Rails.queue[:image_queue] = SomeQueue.new
+ # Rails.queue[:mail_queue] = SomeQueue.new
+ #
+ # # In your app code:
+ # Rails.queue[:mail_queue].push SomeJob.new
+ #
+ class Container < DelegateClass(::Queue)
+ def initialize(default_queue)
+ @queues = { :default => default_queue }
+ super(default_queue)
+ end
+
+ def [](queue_name)
+ @queues[queue_name]
+ end
+
+ def []=(queue_name, queue)
+ @queues[queue_name] = queue
+ end
+ end
+
# A Queue that simply inherits from STDLIB's Queue. Everytime this
# queue is used, Rails automatically sets up a ThreadedConsumer
# to consume it.
@@ -22,6 +49,13 @@ module Rails
@que.dup
end
+ # Marshal and unmarshal job before pushing it onto the queue. This will
+ # raise an exception on any attempts in tests to push jobs that can't (or
+ # shouldn't) be marshalled.
+ def push(job)
+ super Marshal.load(Marshal.dump(job))
+ end
+
# Drain the queue, running all jobs in a different thread. This method
# may not be available on production queues.
def drain
diff --git a/railties/lib/rails/tasks/routes.rake b/railties/lib/rails/tasks/routes.rake
index 4ade825616..95f47566ef 100644
--- a/railties/lib/rails/tasks/routes.rake
+++ b/railties/lib/rails/tasks/routes.rake
@@ -1,7 +1,7 @@
desc 'Print out all defined routes in match order, with names. Target specific controller with CONTROLLER=x.'
task :routes => :environment do
all_routes = Rails.application.routes.routes
- require 'rails/application/routes_inspector'
- inspector = Rails::Application::RoutesInspector.new
+ require 'action_dispatch/routing/inspector'
+ inspector = ActionDispatch::Routing::RoutesInspector.new
puts inspector.format(all_routes, ENV['CONTROLLER']).join "\n"
end
diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb
index 46bf3bbe48..581ceaf9ce 100644
--- a/railties/lib/rails/test_help.rb
+++ b/railties/lib/rails/test_help.rb
@@ -10,7 +10,10 @@ require 'action_dispatch/testing/integration'
# Enable turn if it is available
begin
require 'turn'
- MiniTest::Unit.use_natural_language_case_names = true
+
+ Turn.config do |c|
+ c.natural = true
+ end
rescue LoadError
end
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index ba747bc633..9c9ed0cd6b 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -45,7 +45,7 @@ module ApplicationTests
"ActionDispatch::Session::CookieStore",
"ActionDispatch::Flash",
"ActionDispatch::ParamsParser",
- "ActionDispatch::Head",
+ "Rack::Head",
"Rack::ConditionalGet",
"Rack::ETag",
"ActionDispatch::BestStandardsSupport"
diff --git a/railties/test/application/queue_test.rb b/railties/test/application/queue_test.rb
index da8bdeed52..83ca981336 100644
--- a/railties/test/application/queue_test.rb
+++ b/railties/test/application/queue_test.rb
@@ -20,56 +20,78 @@ module ApplicationTests
test "the queue is a TestQueue in test mode" do
app("test")
- assert_kind_of Rails::Queueing::TestQueue, Rails.application.queue
- assert_kind_of Rails::Queueing::TestQueue, Rails.queue
+ assert_kind_of Rails::Queueing::TestQueue, Rails.application.queue[:default]
+ assert_kind_of Rails::Queueing::TestQueue, Rails.queue[:default]
end
test "the queue is a Queue in development mode" do
app("development")
- assert_kind_of Rails::Queueing::Queue, Rails.application.queue
- assert_kind_of Rails::Queueing::Queue, Rails.queue
+ assert_kind_of Rails::Queueing::Queue, Rails.application.queue[:default]
+ assert_kind_of Rails::Queueing::Queue, Rails.queue[:default]
end
- test "in development mode, an enqueued job will be processed in a separate thread" do
- app("development")
+ class ThreadTrackingJob
+ def initialize
+ @origin = Thread.current.object_id
+ end
+
+ def run
+ @target = Thread.current.object_id
+ end
- job = Struct.new(:origin, :target).new(Thread.current)
- def job.run
- self.target = Thread.current
+ def ran_in_different_thread?
+ @origin != @target
end
+ def ran?
+ @target
+ end
+ end
+
+ test "in development mode, an enqueued job will be processed in a separate thread" do
+ app("development")
+
+ job = ThreadTrackingJob.new
Rails.queue.push job
sleep 0.1
- assert job.target, "The job was run"
- assert_not_equal job.origin, job.target
+ assert job.ran?, "Expected job to be run"
+ assert job.ran_in_different_thread?, "Expected job to run in a different thread"
end
test "in test mode, explicitly draining the queue will process it in a separate thread" do
app("test")
- job = Struct.new(:origin, :target).new(Thread.current)
- def job.run
- self.target = Thread.current
+ Rails.queue.push ThreadTrackingJob.new
+ job = Rails.queue.jobs.last
+ Rails.queue.drain
+
+ assert job.ran?, "Expected job to be run"
+ assert job.ran_in_different_thread?, "Expected job to run in a different thread"
+ end
+
+ class IdentifiableJob
+ def initialize(id)
+ @id = id
end
- Rails.queue.push job
- Rails.queue.drain
+ def ==(other)
+ other.same_id?(@id)
+ end
+
+ def same_id?(other_id)
+ other_id == @id
+ end
- assert job.target, "The job was run"
- assert_not_equal job.origin, job.target
+ def run
+ end
end
test "in test mode, the queue can be observed" do
app("test")
- job = Struct.new(:id) do
- def run
- end
- end
-
jobs = (1..10).map do |id|
- job.new(id)
+ IdentifiableJob.new(id)
end
jobs.each do |job|
@@ -79,6 +101,29 @@ module ApplicationTests
assert_equal jobs, Rails.queue.jobs
end
+ test "in test mode, adding an unmarshallable job will raise an exception" do
+ app("test")
+ anonymous_class_instance = Struct.new(:run).new
+ assert_raises TypeError do
+ Rails.queue.push anonymous_class_instance
+ end
+ end
+
+ test "attempting to marshal a queue will raise an exception" do
+ app("test")
+ assert_raises TypeError do
+ Marshal.dump Rails.queue
+ end
+ end
+
+ test "attempting to add a reference to itself to the queue will raise an exception" do
+ app("test")
+ job = {reference: Rails.queue}
+ assert_raises TypeError do
+ Rails.queue.push job
+ end
+ end
+
def setup_custom_queue
add_to_env_config "production", <<-RUBY
require "my_queue"
@@ -99,7 +144,7 @@ module ApplicationTests
test "a custom queue implementation can be provided" do
setup_custom_queue
- assert_kind_of MyQueue, Rails.queue
+ assert_kind_of MyQueue, Rails.queue[:default]
job = Struct.new(:id, :ran) do
def run
diff --git a/railties/test/application/rack/logger_test.rb b/railties/test/application/rack/logger_test.rb
index 46fd09cb26..2f5bd3a764 100644
--- a/railties/test/application/rack/logger_test.rb
+++ b/railties/test/application/rack/logger_test.rb
@@ -26,12 +26,18 @@ module ApplicationTests
@logs ||= @logger.logged(:info)
end
- test "logger logs proper HTTP verb and path" do
+ test "logger logs proper HTTP GET verb and path" do
get "/blah"
wait
assert_match(/^Started GET "\/blah"/, logs[0])
end
+ test "logger logs proper HTTP HEAD verb and path" do
+ head "/blah"
+ wait
+ assert_match(/^Started HEAD "\/blah"/, logs[0])
+ end
+
test "logger logs HTTP verb override" do
post "/", {:_method => 'put'}
wait
diff --git a/railties/test/application/routes_inspect_test.rb b/railties/test/application/routes_inspect_test.rb
deleted file mode 100644
index 68a8afc93e..0000000000
--- a/railties/test/application/routes_inspect_test.rb
+++ /dev/null
@@ -1,168 +0,0 @@
-require 'minitest/autorun'
-require 'rails/application/routes_inspector'
-require 'action_controller'
-require 'rails/engine'
-
-module ApplicationTests
- class RoutesInspectTest < ActiveSupport::TestCase
- def setup
- @set = ActionDispatch::Routing::RouteSet.new
- @inspector = Rails::Application::RoutesInspector.new
- app = ActiveSupport::OrderedOptions.new
- app.config = ActiveSupport::OrderedOptions.new
- app.config.assets = ActiveSupport::OrderedOptions.new
- app.config.assets.prefix = '/sprockets'
- Rails.stubs(:application).returns(app)
- Rails.stubs(:env).returns("development")
- end
-
- def draw(&block)
- @set.draw(&block)
- @inspector.format(@set.routes)
- end
-
- def test_displaying_routes_for_engines
- engine = Class.new(Rails::Engine) do
- def self.to_s
- "Blog::Engine"
- end
- end
- engine.routes.draw do
- get '/cart', :to => 'cart#show'
- end
-
- output = draw do
- get '/custom/assets', :to => 'custom_assets#show'
- mount engine => "/blog", :as => "blog"
- end
-
- expected = [
- "custom_assets GET /custom/assets(.:format) custom_assets#show",
- " blog /blog Blog::Engine",
- "\nRoutes for Blog::Engine:",
- "cart GET /cart(.:format) cart#show"
- ]
- assert_equal expected, output
- end
-
- def test_cart_inspect
- output = draw do
- get '/cart', :to => 'cart#show'
- end
- assert_equal ["cart GET /cart(.:format) cart#show"], output
- end
-
- def test_inspect_shows_custom_assets
- output = draw do
- get '/custom/assets', :to => 'custom_assets#show'
- end
- assert_equal ["custom_assets GET /custom/assets(.:format) custom_assets#show"], output
- end
-
- def test_inspect_routes_shows_resources_route
- output = draw do
- resources :articles
- end
- expected = [
- " articles GET /articles(.:format) articles#index",
- " POST /articles(.:format) articles#create",
- " new_article GET /articles/new(.:format) articles#new",
- "edit_article GET /articles/:id/edit(.:format) articles#edit",
- " article GET /articles/:id(.:format) articles#show",
- " PATCH /articles/:id(.:format) articles#update",
- " PUT /articles/:id(.:format) articles#update",
- " DELETE /articles/:id(.:format) articles#destroy" ]
- assert_equal expected, output
- end
-
- def test_inspect_routes_shows_root_route
- output = draw do
- root :to => 'pages#main'
- end
- assert_equal ["root GET / pages#main"], output
- end
-
- def test_inspect_routes_shows_dynamic_action_route
- output = draw do
- get 'api/:action' => 'api'
- end
- assert_equal [" GET /api/:action(.:format) api#:action"], output
- end
-
- def test_inspect_routes_shows_controller_and_action_only_route
- output = draw do
- get ':controller/:action'
- end
- assert_equal [" GET /:controller/:action(.:format) :controller#:action"], output
- end
-
- def test_inspect_routes_shows_controller_and_action_route_with_constraints
- output = draw do
- get ':controller(/:action(/:id))', :id => /\d+/
- end
- assert_equal [" GET /:controller(/:action(/:id))(.:format) :controller#:action {:id=>/\\d+/}"], output
- end
-
- def test_rake_routes_shows_route_with_defaults
- output = draw do
- get 'photos/:id' => 'photos#show', :defaults => {:format => 'jpg'}
- end
- assert_equal [%Q[ GET /photos/:id(.:format) photos#show {:format=>"jpg"}]], output
- end
-
- def test_rake_routes_shows_route_with_constraints
- output = draw do
- get 'photos/:id' => 'photos#show', :id => /[A-Z]\d{5}/
- end
- assert_equal [" GET /photos/:id(.:format) photos#show {:id=>/[A-Z]\\d{5}/}"], output
- end
-
- class RackApp
- def self.call(env)
- end
- end
-
- def test_rake_routes_shows_route_with_rack_app
- output = draw do
- get 'foo/:id' => RackApp, :id => /[A-Z]\d{5}/
- end
- assert_equal [" GET /foo/:id(.:format) #{RackApp.name} {:id=>/[A-Z]\\d{5}/}"], output
- end
-
- def test_rake_routes_shows_route_with_rack_app_nested_with_dynamic_constraints
- constraint = Class.new do
- def to_s
- "( my custom constraint )"
- end
- end
-
- output = draw do
- scope :constraint => constraint.new do
- mount RackApp => '/foo'
- end
- end
-
- assert_equal [" /foo #{RackApp.name} {:constraint=>( my custom constraint )}"], output
- end
-
- def test_rake_routes_dont_show_app_mounted_in_assets_prefix
- output = draw do
- get '/sprockets' => RackApp
- end
- assert_no_match(/RackApp/, output.first)
- assert_no_match(/\/sprockets/, output.first)
- end
-
- def test_redirect
- output = draw do
- get "/foo" => redirect("/foo/bar"), :constraints => { :subdomain => "admin" }
- get "/bar" => redirect(path: "/foo/bar", status: 307)
- get "/foobar" => redirect{ "/foo/bar" }
- end
-
- assert_equal " foo GET /foo(.:format) redirect(301, /foo/bar) {:subdomain=>\"admin\"}", output[0]
- assert_equal " bar GET /bar(.:format) redirect(307, path: /foo/bar)", output[1]
- assert_equal "foobar GET /foobar(.:format) redirect(301)", output[2]
- end
- end
-end
diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb
index b320e40654..774038c0e1 100644
--- a/railties/test/generators/migration_generator_test.rb
+++ b/railties/test/generators/migration_generator_test.rb
@@ -76,6 +76,23 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_remove_migration_with_references_options
+ migration = "remove_references_from_books"
+ run_generator [migration, "author:belongs_to", "distributor:references{polymorphic}"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :up, content do |up|
+ assert_match(/remove_reference :books, :author/, up)
+ assert_match(/remove_reference :books, :distributor, polymorphic: true/, up)
+ end
+
+ assert_method :down, content do |down|
+ assert_match(/add_reference :books, :author, index: true/, down)
+ assert_match(/add_reference :books, :distributor, polymorphic: true, index: true/, down)
+ end
+ end
+ end
+
def test_add_migration_with_attributes_and_indices
migration = "add_title_with_index_and_body_to_posts"
run_generator [migration, "title:string:index", "body:text", "user_id:integer:uniq"]
@@ -138,6 +155,31 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_add_migration_with_references_options
+ migration = "add_references_to_books"
+ run_generator [migration, "author:belongs_to", "distributor:references{polymorphic}"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |up|
+ assert_match(/add_reference :books, :author, index: true/, up)
+ assert_match(/add_reference :books, :distributor, polymorphic: true, index: true/, up)
+ end
+ end
+ end
+
+ def test_create_join_table_migration
+ migration = "add_media_join_table"
+ run_generator [migration, "artist_id", "musics:uniq"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |up|
+ assert_match(/create_join_table :artists, :musics/, up)
+ assert_match(/# t.index \[:artist_id, :music_id\]/, up)
+ assert_match(/ t.index \[:music_id, :artist_id\], unique: true/, up)
+ end
+ end
+ end
+
def test_should_create_empty_migrations_if_name_not_start_with_add_or_remove
migration = "create_books"
run_generator [migration, "title:string", "content:text"]
diff --git a/railties/test/generators/namespaced_generators_test.rb b/railties/test/generators/namespaced_generators_test.rb
index 2ae9dc61a7..db2b8af217 100644
--- a/railties/test/generators/namespaced_generators_test.rb
+++ b/railties/test/generators/namespaced_generators_test.rb
@@ -99,7 +99,7 @@ class NamespacedModelGeneratorTest < NamespacedGeneratorTestCase
run_generator ["admin/account"]
assert_file "app/models/test_app/admin.rb", /module TestApp/, /module Admin/
assert_file "app/models/test_app/admin.rb", /def self\.table_name_prefix/
- assert_file "app/models/test_app/admin.rb", /'admin_'/
+ assert_file "app/models/test_app/admin.rb", /'test_app_admin_'/
assert_file "app/models/test_app/admin/account.rb", /module TestApp/, /class Admin::Account < ActiveRecord::Base/
end
diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb
index 0a235b56d5..6c3e0178f2 100644
--- a/railties/test/generators/plugin_new_generator_test.rb
+++ b/railties/test/generators/plugin_new_generator_test.rb
@@ -264,11 +264,14 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
assert_file "spec/dummy"
assert_file "spec/dummy/config/application.rb"
assert_no_file "test"
+ assert_file '.gitignore' do |contents|
+ assert_match(/spec\/dummy/, contents)
+ end
end
def test_ensure_that_gitignore_can_be_generated_from_a_template_for_dummy_path
FileUtils.cd(Rails.root)
- run_generator([destination_root, "--dummy_path", "spec/dummy" "--skip-test-unit"])
+ run_generator([destination_root, "--dummy_path", "spec/dummy", "--skip-test-unit"])
assert_file ".gitignore" do |contents|
assert_match(/spec\/dummy/, contents)
end
@@ -280,6 +283,9 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
assert_file "bukkits.gemspec" do |contents|
assert_no_match(/s.test_files = Dir\["test\/\*\*\/\*"\]/, contents)
end
+ assert_file '.gitignore' do |contents|
+ assert_no_match(/test\dummy/, contents)
+ end
end
def test_skipping_gemspec
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index 417d019178..027d8eb9b7 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -1,7 +1,6 @@
require 'generators/generators_test_helper'
require 'rails/generators/rails/model/model_generator'
require 'rails/generators/test_unit/model/model_generator'
-require 'mocha'
class GeneratorsTest < Rails::Generators::TestCase
include GeneratorsTestHelper
@@ -168,7 +167,7 @@ class GeneratorsTest < Rails::Generators::TestCase
def test_developer_options_are_overwriten_by_user_options
Rails::Generators.options[:with_options] = { :generate => false }
- self.class.class_eval <<-end_eval
+ self.class.class_eval(<<-end_eval, __FILE__, __LINE__ + 1)
class WithOptionsGenerator < Rails::Generators::Base
class_option :generate, :default => true
end
diff --git a/railties/test/queueing/container_test.rb b/railties/test/queueing/container_test.rb
new file mode 100644
index 0000000000..69e59a3871
--- /dev/null
+++ b/railties/test/queueing/container_test.rb
@@ -0,0 +1,30 @@
+require 'abstract_unit'
+require 'rails/queueing'
+
+module Rails
+ module Queueing
+ class ContainerTest < ActiveSupport::TestCase
+ def test_delegates_to_default
+ q = Queue.new
+ container = Container.new q
+ job = Object.new
+
+ container.push job
+ assert_equal job, q.pop
+ end
+
+ def test_access_default
+ q = Queue.new
+ container = Container.new q
+ assert_equal q, container[:default]
+ end
+
+ def test_assign_queue
+ container = Container.new Object.new
+ q = Object.new
+ container[:foo] = q
+ assert_equal q, container[:foo]
+ end
+ end
+ end
+end
diff --git a/railties/test/queueing/test_queue_test.rb b/railties/test/queueing/test_queue_test.rb
index 78c6c617fe..2f0f507adb 100644
--- a/railties/test/queueing/test_queue_test.rb
+++ b/railties/test/queueing/test_queue_test.rb
@@ -2,22 +2,18 @@ require 'abstract_unit'
require 'rails/queueing'
class TestQueueTest < ActiveSupport::TestCase
- class Job
- def initialize(&block)
- @block = block
- end
+ def setup
+ @queue = Rails::Queueing::TestQueue.new
+ end
+ class ExceptionRaisingJob
def run
- @block.call if @block
+ raise
end
end
- def setup
- @queue = Rails::Queueing::TestQueue.new
- end
-
def test_drain_raises
- @queue.push Job.new { raise }
+ @queue.push ExceptionRaisingJob.new
assert_raises(RuntimeError) { @queue.drain }
end
@@ -27,41 +23,80 @@ class TestQueueTest < ActiveSupport::TestCase
assert_equal [1,2], @queue.jobs
end
+ class EquivalentJob
+ def initialize
+ @initial_id = self.object_id
+ end
+
+ def run
+ end
+
+ def ==(other)
+ other.same_initial_id?(@initial_id)
+ end
+
+ def same_initial_id?(other_id)
+ other_id == @initial_id
+ end
+ end
+
def test_contents
assert @queue.empty?
- job = Job.new
+ job = EquivalentJob.new
@queue.push job
refute @queue.empty?
assert_equal job, @queue.pop
end
- def test_order
- processed = []
+ class ProcessingJob
+ def self.clear_processed
+ @processed = []
+ end
+
+ def self.processed
+ @processed
+ end
+
+ def initialize(object)
+ @object = object
+ end
+
+ def run
+ self.class.processed << @object
+ end
+ end
- job1 = Job.new { processed << 1 }
- job2 = Job.new { processed << 2 }
+ def test_order
+ ProcessingJob.clear_processed
+ job1 = ProcessingJob.new(1)
+ job2 = ProcessingJob.new(2)
@queue.push job1
@queue.push job2
@queue.drain
- assert_equal [1,2], processed
+ assert_equal [1,2], ProcessingJob.processed
end
- def test_drain
- t = nil
- ran = false
+ class ThreadTrackingJob
+ attr_reader :thread_id
- job = Job.new do
- ran = true
- t = Thread.current
+ def run
+ @thread_id = Thread.current.object_id
end
- @queue.push job
+ def ran?
+ @thread_id
+ end
+ end
+
+ def test_drain
+ @queue.push ThreadTrackingJob.new
+ job = @queue.jobs.last
@queue.drain
assert @queue.empty?
- assert ran, "The job runs synchronously when the queue is drained"
- assert_not_equal t, Thread.current
+ assert job.ran?, "The job runs synchronously when the queue is drained"
+ assert_not_equal job.thread_id, Thread.current.object_id
end
end
diff --git a/railties/test/railties/generators_test.rb b/railties/test/railties/generators_test.rb
index 1938bfb6c2..c90b795d59 100644
--- a/railties/test/railties/generators_test.rb
+++ b/railties/test/railties/generators_test.rb
@@ -75,6 +75,18 @@ module RailtiesTests
end
end
+ def test_table_name_prefix_is_correctly_namespaced_when_engine_is_mountable
+ build_mountable_engine
+ Dir.chdir(engine_path) do
+ bundled_rails("g model namespaced/topic")
+ assert_file "app/models/foo_bar/namespaced.rb", /module FooBar\n module Namespaced/ do |content|
+ assert_class_method :table_name_prefix, content do |method_content|
+ assert_match(/'foo_bar_namespaced_'/, method_content)
+ end
+ end
+ end
+ end
+
def test_helpers_are_correctly_namespaced_when_engine_is_mountable
build_mountable_engine
Dir.chdir(engine_path) do