diff options
Diffstat (limited to 'guides/source')
49 files changed, 3188 insertions, 1733 deletions
diff --git a/guides/source/3_2_release_notes.md b/guides/source/3_2_release_notes.md index c52c39b705..f16d509f77 100644 --- a/guides/source/3_2_release_notes.md +++ b/guides/source/3_2_release_notes.md @@ -154,7 +154,7 @@ Railties rails g scaffold Post title:string:index author:uniq price:decimal{7,2} ``` - will create indexes for `title` and `author` with the latter being an unique index. Some types such as decimal accept custom options. In the example, `price` will be a decimal column with precision and scale set to 7 and 2 respectively. + will create indexes for `title` and `author` with the latter being a unique index. Some types such as decimal accept custom options. In the example, `price` will be a decimal column with precision and scale set to 7 and 2 respectively. * Turn gem has been removed from default Gemfile. @@ -327,7 +327,7 @@ Active Record * Implemented `ActiveRecord::Relation#explain`. -* Implements `AR::Base.silence_auto_explain` which allows the user to selectively disable automatic EXPLAINs within a block. +* Implements `ActiveRecord::Base.silence_auto_explain` which allows the user to selectively disable automatic EXPLAINs within a block. * Implements automatic EXPLAIN logging for slow queries. A new configuration parameter `config.active_record.auto_explain_threshold_in_seconds` determines what's to be considered a slow query. Setting that to nil disables this feature. Defaults are 0.5 in development mode, and nil in test and production modes. Rails 3.2 supports this feature in SQLite, MySQL (mysql2 adapter), and PostgreSQL. diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md index 684bd286bc..8a59007420 100644 --- a/guides/source/4_2_release_notes.md +++ b/guides/source/4_2_release_notes.md @@ -227,6 +227,17 @@ restore the old behavior. If you do this, be sure to configure your firewall properly such that only trusted machines on your network can access your development server. +### Changed status option symbols for `render` + +Due to a [change in Rack](https://github.com/rack/rack/commit/be28c6a2ac152fe4adfbef71f3db9f4200df89e8), the symbols that the `render` method accepts for the `:status` option have changed: + +- 306: `:reserved` has been removed. +- 413: `:request_entity_too_large` has been renamed to `:payload_too_large`. +- 414: `:request_uri_too_long` has been renamed to `:uri_too_long`. +- 416: `:requested_range_not_satisfiable` has been renamed to `:range_not_satisfiable`. + +Keep in mind that if calling `render` with an unknown symbol, the response status will default to 500. + ### HTML Sanitizer The HTML sanitizer has been replaced with a new, more robust, implementation diff --git a/guides/source/5_0_release_notes.md b/guides/source/5_0_release_notes.md new file mode 100644 index 0000000000..f45c8005da --- /dev/null +++ b/guides/source/5_0_release_notes.md @@ -0,0 +1,771 @@ +**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.** + +Ruby on Rails 5.0 Release Notes +=============================== + +Highlights in Rails 5.0: + +* Action Cable +* Rails API +* Active Record Attributes API +* Test Runner +* Exclusive use of `rails` CLI over Rake +* Sprockets 3 +* Turbolinks 5 +* Ruby 2.2.2+ required + +These release notes cover only the major changes. To learn about various bug +fixes and changes, please refer to the change logs or check out the [list of +commits](https://github.com/rails/rails/commits/5-0-stable) in the main Rails +repository on GitHub. + +-------------------------------------------------------------------------------- + +Upgrading to Rails 5.0 +---------------------- + +If you're upgrading an existing application, it's a great idea to have good test +coverage before going in. You should also first upgrade to Rails 4.2 in case you +haven't and make sure your application still runs as expected before attempting +an update to Rails 5.0. A list of things to watch out for when upgrading is +available in the +[Upgrading Ruby on Rails](upgrading_ruby_on_rails.html#upgrading-from-rails-4-2-to-rails-5-0) +guide. + + +Major Features +-------------- + +### Action Cable +[Pull Request](https://github.com/rails/rails/pull/22586) + +ToDo... + +### Rails API +[Pull Request](https://github.com/rails/rails/pull/19832) + +ToDo... + +### Active Record attributes API + +ToDo... + +### Test Runner +[Pull Request](https://github.com/rails/rails/pull/19216) + +ToDo... + + +Railties +-------- + +Please refer to the [Changelog][railties] for detailed changes. + +### Removals + +* Removed debugger support, use byebug instead. `debugger` is not supported by + Ruby + 2.2. ([commit](https://github.com/rails/rails/commit/93559da4826546d07014f8cfa399b64b4a143127)) + +* Removed deprecated `test:all` and `test:all:db` tasks. + ([commit](https://github.com/rails/rails/commit/f663132eef0e5d96bf2a58cec9f7c856db20be7c)) + +* Removed deprecated `Rails::Rack::LogTailer`. + ([commit](https://github.com/rails/rails/commit/c564dcb75c191ab3d21cc6f920998b0d6fbca623)) + +* Removed deprecated `RAILS_CACHE` constant. + ([commit](https://github.com/rails/rails/commit/b7f856ce488ef8f6bf4c12bb549f462cb7671c08)) + +* Removed deprecated `serve_static_assets` configuration. + ([commit](https://github.com/rails/rails/commit/463b5d7581ee16bfaddf34ca349b7d1b5878097c)) + +* Removed the documentation tasks `doc:app`, `doc:rails`, and `doc:guides`. + ([commit](https://github.com/rails/rails/commit/cd7cc5254b090ccbb84dcee4408a5acede25ef2a)) + +* Removed `Rack::ContentLength` middleware from the default + stack. ([Commit](https://github.com/rails/rails/commit/56903585a099ab67a7acfaaef0a02db8fe80c450)) + +### Deprecations + +* Deprecated `config.static_cache_control` in favor of + `config.public_file_server.headers`. + ([Pull Request](https://github.com/rails/rails/pull/22173)) + +* Deprecated `config.serve_static_files` in favor of `config.public_file_server.enabled`. + ([Pull Request](https://github.com/rails/rails/pull/22173)) + +### Notable changes + +* Added Rails test runner `bin/rails test`. + ([Pull Request](https://github.com/rails/rails/pull/19216)) + +* Newly generated applications and plugins get a `README.md` in Markdown. + ([commit](https://github.com/rails/rails/commit/89a12c931b1f00b90e74afffcdc2fc21f14ca663), + [Pull Request](https://github.com/rails/rails/pull/22068)) + +* Added `bin/rails restart` task to restart your Rails app by touching `tmp/restart.txt`. + ([Pull Request](https://github.com/rails/rails/pull/18965)) + +* Added `bin/rails initializers` task to print out all defined initializers in + the order they are invoked by Rails. + ([Pull Request](https://github.com/rails/rails/pull/19323)) + +* Added `bin/rails dev:cache` to enable or disable caching in development mode. + ([Pull Request](https://github.com/rails/rails/pull/20961)) + +* Added `bin/update` script to update the development environment automatically. + ([Pull Request](https://github.com/rails/rails/pull/20972)) + +* Proxy Rake tasks through `bin/rails`. + ([Pull Request](https://github.com/rails/rails/pull/22457), + [Pull Request](https://github.com/rails/rails/pull/22288)) + + +Action Pack +----------- + +Please refer to the [Changelog][action-pack] for detailed changes. + +### Removals + +* Removed `ActionDispatch::Request::Utils.deep_munge`. + ([commit](https://github.com/rails/rails/commit/52cf1a71b393486435fab4386a8663b146608996)) + +* Removed `ActionController::HideActions`. + ([Pull Request](https://github.com/rails/rails/pull/18371)) + +* Removed `respond_to` and `respond_with` placeholder methods, this functionality + has been extracted to the + [responders](https://github.com/plataformatec/responders) gem. + ([commit](https://github.com/rails/rails/commit/afd5e9a7ff0072e482b0b0e8e238d21b070b6280)) + +* Removed deprecated assertion files. + ([commit](https://github.com/rails/rails/commit/92e27d30d8112962ee068f7b14aa7b10daf0c976)) + +* Removed deprecated usage of string keys in URL helpers. + ([commit](https://github.com/rails/rails/commit/34e380764edede47f7ebe0c7671d6f9c9dc7e809)) + +* Removed deprecated `only_path` option on `*_path` helpers. + ([commit](https://github.com/rails/rails/commit/e4e1fd7ade47771067177254cb133564a3422b8a)) + +* Removed deprecated `NamedRouteCollection#helpers`. + ([commit](https://github.com/rails/rails/commit/2cc91c37bc2e32b7a04b2d782fb8f4a69a14503f)) + +* Removed deprecated support to define routes with `:to` option that doesn't contain `#`. + ([commit](https://github.com/rails/rails/commit/1f3b0a8609c00278b9a10076040ac9c90a9cc4a6)) + +* Removed deprecated `ActionDispatch::Response#to_ary`. + ([commit](https://github.com/rails/rails/commit/4b19d5b7bcdf4f11bd1e2e9ed2149a958e338c01)) + +* Removed deprecated `ActionDispatch::Request#deep_munge`. + ([commit](https://github.com/rails/rails/commit/7676659633057dacd97b8da66e0d9119809b343e)) + +* Removed deprecated + `ActionDispatch::Http::Parameters#symbolized_path_parameters`. + ([commit](https://github.com/rails/rails/commit/7fe7973cd8bd119b724d72c5f617cf94c18edf9e)) + +* Removed deprecated option `use_route` in controller tests. + ([commit](https://github.com/rails/rails/commit/e4cfd353a47369dd32198b0e67b8cbb2f9a1c548)) + +* Removed `assigns` and `assert_template`. Both methods have been extracted + into the + [rails-controller-testing](https://github.com/rails/rails-controller-testing) + gem. + ([Pull Request](https://github.com/rails/rails/pull/20138)) + +### Deprecations + +* Deprecated all `*_filter` callbacks in favor of `*_action` callbacks. + ([Pull Request](https://github.com/rails/rails/pull/18410)) + +* Deprecated `*_via_redirect` integration test methods. Use `follow_redirect!` + manually after the request call for the same behavior. + ([Pull Request](https://github.com/rails/rails/pull/18693)) + +* Deprecated `AbstractController#skip_action_callback` in favor of individual + skip_callback methods. + ([Pull Request](https://github.com/rails/rails/pull/19060)) + +* Deprecated `:nothing` option for `render` method. + ([Pull Request](https://github.com/rails/rails/pull/20336)) + +* Deprecated passing first parameter as `Hash` and default status code for + `head` method. + ([Pull Request](https://github.com/rails/rails/pull/20407)) + +* Deprecated using strings or symbols for middleware class names. Use class + names instead. + ([commit](https://github.com/rails/rails/commit/83b767ce)) + +* Deprecated accessing mime types via constants (eg. `Mime::HTML`). Use the + subscript operator with a symbol instead (eg. `Mime[:html]`). + ([Pull Request](https://github.com/rails/rails/pull/21869)) + +* Deprecated `redirect_to :back` in favor of `redirect_back`, which accepts a + required `fallback_location` argument, thus eliminating the possibility of a + `RedirectBackError`. + ([Pull Request](https://github.com/rails/rails/pull/22506)) + +### Notable changes + +* Added `ActionController::Renderer` to render arbitrary templates + outside controller actions. + ([Pull Request](https://github.com/rails/rails/pull/18546)) + +* Migrating to keyword arguments syntax in `ActionController::TestCase` and + `ActionDispatch::Integration` HTTP request methods. + ([Pull Request](https://github.com/rails/rails/pull/18323)) + +* Added `http_cache_forever` to Action Controller, so we can cache a response + that never gets expired. + ([Pull Request](https://github.com/rails/rails/pull/18394)) + +* Provide friendlier access to request variants. + ([Pull Request](https://github.com/rails/rails/pull/18939)) + +* For actions with no corresponding templates, render `head :no_content` + instead of raising an error. + ([Pull Request](https://github.com/rails/rails/pull/19377)) + +* Added the ability to override default form builder for a controller. + ([Pull Request](https://github.com/rails/rails/pull/19736)) + +* Added support for API only apps. + `ActionController::API` is added as a replacement of + `ActionController::Base` for this kind of applications. + ([Pull Request](https://github.com/rails/rails/pull/19832)) + +* Make `ActionController::Parameters` no longer inherits from + `HashWithIndifferentAccess`. + ([Pull Request](https://github.com/rails/rails/pull/20868)) + +* Make it easier to opt in to `config.force_ssl` and `config.ssl_options` by + making them less dangerous to try and easier to disable. + ([Pull Request](https://github.com/rails/rails/pull/21520)) + +* Added the ability of returning arbitrary headers to `ActionDispatch::Static`. + ([Pull Request](https://github.com/rails/rails/pull/19135)) + +* Changed the `protect_from_forgery` prepend default to `false`. + ([commit](https://github.com/rails/rails/commit/39794037817703575c35a75f1961b01b83791191)) + +* `ActionController::TestCase` will be moved to it's own gem in Rails 5.1. Use + `ActionDispatch::IntegrationTest` instead. + ([commit](https://github.com/rails/rails/commit/4414c5d1795e815b102571425974a8b1d46d932d)) + +* Rails will only generate "weak", instead of strong ETags. + ([Pull Request](https://github.com/rails/rails/pull/17573)) + +Action View +------------- + +Please refer to the [Changelog][action-view] for detailed changes. + +### Removals + +* Removed deprecated `AbstractController::Base::parent_prefixes`. + ([commit](https://github.com/rails/rails/commit/34bcbcf35701ca44be559ff391535c0dd865c333)) + +* Removed `ActionView::Helpers::RecordTagHelper`, this functionality + has been extracted to the + [record_tag_helper](https://github.com/rails/record_tag_helper) gem. + ([Pull Request](https://github.com/rails/rails/pull/18411)) + +* Removed `:rescue_format` option for `translate` helper since it's no longer + supported by I18n. + ([Pull Request](https://github.com/rails/rails/pull/20019)) + +### Notable Changes + +* Changed the default template handler from `ERB` to `Raw`. + ([commit](https://github.com/rails/rails/commit/4be859f0fdf7b3059a28d03c279f03f5938efc80)) + +* Collection rendering automatically caches and fetches multiple partials. + ([Pull Request](https://github.com/rails/rails/pull/18948)) + +* Allow defining explicit collection caching using a `# Template Collection: ...` + directive inside templates. + ([Pull Request](https://github.com/rails/rails/pull/20781)) + +* Added wildcard matching to explicit dependencies. + ([Pull Request](https://github.com/rails/rails/pull/20904)) + +* Make `disable_with` the default behavior for submit tags. Disables the + button on submit to prevent double submits. + ([Pull Request](https://github.com/rails/rails/pull/21135)) + + +Action Mailer +------------- + +Please refer to the [Changelog][action-mailer] for detailed changes. + +### Removals + +* Removed deprecated `*_path` helpers in email views. + ([commit](https://github.com/rails/rails/commit/d282125a18c1697a9b5bb775628a2db239142ac7)) + +* Removed deprecated `deliver` and `deliver!` methods. + ([commit](https://github.com/rails/rails/commit/755dcd0691f74079c24196135f89b917062b0715)) + +### Notable changes + +* Template lookup now respects default locale and I18n fallbacks. + ([commit](https://github.com/rails/rails/commit/ecb1981b)) + +* Added `_mailer` suffix to mailers created via generator, following the same + naming convention used in controllers and jobs. + ([Pull Request](https://github.com/rails/rails/pull/18074)) + +* Added `assert_enqueued_emails` and `assert_no_enqueued_emails`. + ([Pull Request](https://github.com/rails/rails/pull/18403)) + +* Added `config.action_mailer.deliver_later_queue_name` configuration to set + the mailer queue name. + ([Pull Request](https://github.com/rails/rails/pull/18587)) + + +Active Record +------------- + +Please refer to the [Changelog][active-record] for detailed changes. + +### Removals + +* Removed deprecated behavior allowing nested arrays to be passed as query + values. ([Pull Request](https://github.com/rails/rails/pull/17919)) + +* Removed deprecated `ActiveRecord::Tasks::DatabaseTasks#load_schema`. This + method was replaced by `ActiveRecord::Tasks::DatabaseTasks#load_schema_for`. + ([commit](https://github.com/rails/rails/commit/ad783136d747f73329350b9bb5a5e17c8f8800da)) + +* Removed deprecated `serialized_attributes`. + ([commit](https://github.com/rails/rails/commit/82043ab53cb186d59b1b3be06122861758f814b2)) + +* Removed deprecated automatic counter caches on `has_many :through`. + ([commit](https://github.com/rails/rails/commit/87c8ce340c6c83342df988df247e9035393ed7a0)) + +* Removed deprecated `sanitize_sql_hash_for_conditions`. + ([commit](https://github.com/rails/rails/commit/3a59dd212315ebb9bae8338b98af259ac00bbef3)) + +* Removed deprecated `Reflection#source_macro`. + ([commit](https://github.com/rails/rails/commit/ede8c199a85cfbb6457d5630ec1e285e5ec49313)) + +* Removed deprecated `symbolized_base_class` and `symbolized_sti_name`. + ([commit](https://github.com/rails/rails/commit/9013e28e52eba3a6ffcede26f85df48d264b8951)) + +* Removed deprecated `ActiveRecord::Base.disable_implicit_join_references=`. + ([commit](https://github.com/rails/rails/commit/0fbd1fc888ffb8cbe1191193bf86933110693dfc)) + +* Removed deprecated access to connection specification using a string accessor. + ([commit](https://github.com/rails/rails/commit/efdc20f36ccc37afbb2705eb9acca76dd8aabd4f)) + +* Removed deprecated support to preload instance-dependent associations. + ([commit](https://github.com/rails/rails/commit/4ed97979d14c5e92eb212b1a629da0a214084078)) + +* Removed deprecated support for PostgreSQL ranges with exclusive lower bounds. + ([commit](https://github.com/rails/rails/commit/a076256d63f64d194b8f634890527a5ed2651115)) + +* Removed deprecation when modifying a relation with cached Arel. + This raises an `ImmutableRelation` error instead. + ([commit](https://github.com/rails/rails/commit/3ae98181433dda1b5e19910e107494762512a86c)) + +* Removed `ActiveRecord::Serialization::XmlSerializer` from core. This feature + has been extracted into the + [activemodel-serializers-xml](https://github.com/rails/activemodel-serializers-xml) + gem. ([Pull Request](https://github.com/rails/rails/pull/21161)) + +* Removed support for the legacy `mysql` database adapter from core. It will + live on in a separate gem for now, but most users should just use `mysql2`. + +* Removed support for the `protected_attributes` gem. + ([commit](https://github.com/rails/rails/commit/f4fbc0301021f13ae05c8e941c8efc4ae351fdf9)) + +### Deprecations + +* Deprecated passing a class as a value in a query. Users should pass strings + instead. ([Pull Request](https://github.com/rails/rails/pull/17916)) + +* Deprecated returning `false` as a way to halt Active Record callback + chains. The recommended way is to + `throw(:abort)`. ([Pull Request](https://github.com/rails/rails/pull/17227)) + +* Deprecated `ActiveRecord::Base.errors_in_transactional_callbacks=`. + ([commit](https://github.com/rails/rails/commit/07d3d402341e81ada0214f2cb2be1da69eadfe72)) + +* Deprecated passing of `start` value to `find_in_batches` and `find_each` + in favour of `begin_at` value. + ([Pull Request](https://github.com/rails/rails/pull/18961)) + +* Deprecated `Relation#uniq` use `Relation#distinct` instead. + ([commit](https://github.com/rails/rails/commit/adfab2dcf4003ca564d78d4425566dd2d9cd8b4f)) + +* Deprecated the PostgreSQL `:point` type in favor of a new one which will return + `Point` objects instead of an `Array` + ([Pull Request](https://github.com/rails/rails/pull/20448)) + +* Deprecated force association reload by passing a truthy argument to + association method. + ([Pull Request](https://github.com/rails/rails/pull/20888)) + +* Deprecated the keys for association `restrict_dependent_destroy` errors in favor + of new key names. + ([Pull Request](https://github.com/rails/rails/pull/20668)) + +* Synchronize behavior of `#tables`. + ([Pull Request](https://github.com/rails/rails/pull/21601)) + +* Deprecated `SchemaCache#tables`, `SchemaCache#table_exists?` and + `SchemaCache#clear_table_cache!` in favor of their new data source + counterparts. + ([Pull Request](https://github.com/rails/rails/pull/21715)) + +* Deprecated `connection.tables` on the SQLite3 and MySQL adapters. + ([Pull Request](https://github.com/rails/rails/pull/21601)) + +* Deprecated passing arguments to `#tables` - the `#tables` method of some + adapters (mysql2, sqlite3) would return both tables and views while others + (postgresql) just return tables. To make their behavior consistent, + `#tables` will return only tables in the future. + ([Pull Request](https://github.com/rails/rails/pull/21601)) + +* Deprecated `table_exists?` - The `#table_exists?` method would check both + tables and views. To make their behavior consistent with `#tables`, + `#table_exists?` will check only tables in the future. + ([Pull Request](https://github.com/rails/rails/pull/21601)) + +* Deprecate sending the `offset` argument to `find_nth`. Please use the + `offset` method on relation instead. + ([Pull Request](https://github.com/rails/rails/pull/22053)) + +### Notable changes + +* Added a `foreign_key` option to `references` while creating the table. + ([commit](https://github.com/rails/rails/commit/99a6f9e60ea55924b44f894a16f8de0162cf2702)) + +* New attributes + API. ([commit](https://github.com/rails/rails/commit/8c752c7ac739d5a86d4136ab1e9d0142c4041e58)) + +* Added `:enum_prefix`/`:enum_suffix` option to `enum` + definition. ([Pull Request](https://github.com/rails/rails/pull/19813)) + +* Added `#cache_key` to `ActiveRecord::Relation`. + ([Pull Request](https://github.com/rails/rails/pull/20884)) + +* Added `ActiveRecord::Relation#outer_joins`. + ([Pull Request](https://github.com/rails/rails/pull/12071)) + +* Require `belongs_to` by default. + ([Pull Request](https://github.com/rails/rails/pull/18937)) - Deprecate + `required` option in favor of `optional` for `belongs_to` + +* Changed the default `null` value for `timestamps` to `false`. + ([commit](https://github.com/rails/rails/commit/a939506f297b667291480f26fa32a373a18ae06a)) + +* Added `ActiveRecord::SecureToken` in order to encapsulate generation of + unique tokens for attributes in a model using `SecureRandom`. + ([Pull Request](https://github.com/rails/rails/pull/18217)) + +* Added `:if_exists` option for `drop_table`. + ([Pull Request](https://github.com/rails/rails/pull/18597)) + +* Added `ActiveRecord::Base#accessed_fields`, which can be used to quickly + discover which fields were read from a model when you are looking to only + select the data you need from the database. + ([commit](https://github.com/rails/rails/commit/be9b68038e83a617eb38c26147659162e4ac3d2c)) + +* Added the `#or` method on `ActiveRecord::Relation`, allowing use of the OR + operator to combine WHERE or HAVING clauses. + ([commit](https://github.com/rails/rails/commit/b0b37942d729b6bdcd2e3178eda7fa1de203b3d0)) + +* Added `:time` option added for `#touch`. + ([Pull Request](https://github.com/rails/rails/pull/18956)) + +* Added `ActiveRecord::Base.suppress` to prevent the receiver from being saved + during the given block. + ([Pull Request](https://github.com/rails/rails/pull/18910)) + +* `belongs_to` will now trigger a validation error by default if the + association is not present. You can turn this off on a per-association basis + with `optional: true`. + ([Pull Request](https://github.com/rails/rails/pull/18937)) + +* Added `config.active_record.dump_schemas` to configure the behavior of + `db:structure:dump`. + ([Pull Request](https://github.com/rails/rails/pull/19347)) + +* Added `config.active_record.warn_on_records_fetched_greater_than` option. + ([Pull Request](https://github.com/rails/rails/pull/18846)) + +* Added a native JSON data type support in MySQL. + ([Pull Request](https://github.com/rails/rails/pull/21110)) + +* Added support for dropping indexes concurrently in PostgreSQL. + ([Pull Request](https://github.com/rails/rails/pull/21317)) + +* Added `#views` and `#view_exists?` methods on connection adapters. + ([Pull Request](https://github.com/rails/rails/pull/21609)) + +* Added `ActiveRecord::Base.ignored_columns` to make some columns + invisible from Active Record. + ([Pull Request](https://github.com/rails/rails/pull/21720)) + +* Added `connection.data_sources` and `connection.data_source_exists?`. + These methods determine what relations can be used to back Active Record + models (usually tables and views). + ([Pull Request](https://github.com/rails/rails/pull/21715)) + +* Allow fixtures files to set the model class in the YAML file itself. + ([Pull Request](https://github.com/rails/rails/pull/20574)) + +* Added ability to default to `uuid` as primary key when generating database + migrations. ([Pull Request](https://github.com/rails/rails/pull/21762)) + +* Added `ActiveRecord::Relation#left_joins` and + `ActiveRecord::Relation#left_outer_joins`. + ([Pull Request](https://github.com/rails/rails/pull/12071)) + +* Added `after_{create,update,delete}_commit` callbacks. + ([Pull Request](https://github.com/rails/rails/pull/22516)) + +* Version the API presented to migration classes, so we can change parameter + defaults without breaking existing migrations, or forcing them to be + rewritten through a deprecation cycle. + ([Pull Request](https://github.com/rails/rails/pull/21538)) + +* `ApplicationRecord` is a new superclass for all app models, analogous to app + controllers subclassing `ApplicationController` instead of + `ActionController::Base`. This gives apps a single spot to configure app-wide + model behavior. + ([Pull Request](https://github.com/rails/rails/pull/22567)) + + +Active Model +------------ + +Please refer to the [Changelog][active-model] for detailed changes. + +### Removals + +* Removed deprecated `ActiveModel::Dirty#reset_#{attribute}` and + `ActiveModel::Dirty#reset_changes`. + ([Pull Request](https://github.com/rails/rails/commit/37175a24bd508e2983247ec5d011d57df836c743)) + +* Removed XML serialization. This feature has been extracted into the + [activemodel-serializers-xml](https://github.com/rails/activemodel-serializers-xml) gem. + ([Pull Request](https://github.com/rails/rails/pull/21161)) + +### Deprecations + +* Deprecated returning `false` as a way to halt Active Model and + `ActiveModel::Validations` callback chains. The recommended way is to + `throw(:abort)`. ([Pull Request](https://github.com/rails/rails/pull/17227)) + +* Deprecated `ActiveModel::Errors#get`, `ActiveModel::Errors#set` and + `ActiveModel::Errors#[]=` methods that have inconsistent behavior. + ([Pull Request](https://github.com/rails/rails/pull/18634)) + +* Deprecated the `:tokenizer` option for `validates_length_of`, in favor of + plain Ruby. + ([Pull Request](https://github.com/rails/rails/pull/19585)) + +* Deprecated `ActiveModel::Errors#add_on_empty` and `ActiveModel::Errors#add_on_blank` + with no replacement. + ([Pull Request](https://github.com/rails/rails/pull/18996)) + +### Notable changes + +* Added `ActiveModel::Errors#details` to determine what validator has failed. + ([Pull Request](https://github.com/rails/rails/pull/18322)) + +* Extracted `ActiveRecord::AttributeAssignment` to `ActiveModel::AttributeAssignment` + allowing to use it for any object as an includable module. + ([Pull Request](https://github.com/rails/rails/pull/10776)) + +* Added `ActiveModel::Dirty#[attr_name]_previously_changed?` and + `ActiveModel::Dirty#[attr_name]_previous_change` to improve access + to recorded changes after the model has been saved. + ([Pull Request](https://github.com/rails/rails/pull/19847)) + +* Validate multiple contexts on `valid?` and `invalid?` at once. + ([Pull Request](https://github.com/rails/rails/pull/21069)) + + +Active Job +----------- + +Please refer to the [Changelog][active-job] for detailed changes. + +### Notable changes + +* `ActiveJob::Base.deserialize` delegates to the job class. this allows jobs + to attach arbitrary metadata when they get serialized and read it back when + they get performed. + ([Pull Request](https://github.com/rails/rails/pull/18260)) + +* A generated job now inherits from `app/jobs/application_job.rb` by default. + ([Pull Request](https://github.com/rails/rails/pull/19034)) + +* Allow `DelayedJob`, `Sidekiq`, `qu`, and `que` to report the job id back to + `ActiveJob::Base` as `provider_job_id`. + ([Pull Request](https://github.com/rails/rails/pull/20064), + [Pull Request](https://github.com/rails/rails/pull/20056)) + +* Implement a simple `AsyncJob` processor and associated `AsyncAdapter` that + queue jobs to a `concurrent-ruby` thread pool. + ([Pull Request](https://github.com/rails/rails/pull/21257)) + + +Active Support +-------------- + +Please refer to the [Changelog][active-support] for detailed changes. + +### Removals + +* Removed deprecated `ActiveSupport::JSON::Encoding::CircularReferenceError`. + ([commit](https://github.com/rails/rails/commit/d6e06ea8275cdc3f126f926ed9b5349fde374b10)) + +* Removed deprecated methods `ActiveSupport::JSON::Encoding.encode_big_decimal_as_string=` + and `ActiveSupport::JSON::Encoding.encode_big_decimal_as_string`. + ([commit](https://github.com/rails/rails/commit/c8019c0611791b2716c6bed48ef8dcb177b7869c)) + +* Removed deprecated `ActiveSupport::SafeBuffer#prepend`. + ([commit](https://github.com/rails/rails/commit/e1c8b9f688c56aaedac9466a4343df955b4a67ec)) + +* Removed deprecated methods from `Kernel`. `silence_stderr`, `silence_stream`, + `capture` and `quietly`. + ([commit](https://github.com/rails/rails/commit/481e49c64f790e46f4aff3ed539ed227d2eb46cb)) + +* Removed deprecated `active_support/core_ext/big_decimal/yaml_conversions` + file. + ([commit](https://github.com/rails/rails/commit/98ea19925d6db642731741c3b91bd085fac92241)) + +* Removed deprecated methods `ActiveSupport::Cache::Store.instrument` and + `ActiveSupport::Cache::Store.instrument=`. + ([commit](https://github.com/rails/rails/commit/a3ce6ca30ed0e77496c63781af596b149687b6d7)) + +* Removed deprecated `Class#superclass_delegating_accessor`. + Use `Class#class_attribute` instead. + ([Pull Request](https://github.com/rails/rails/pull/16938)) + +* Removed deprecated `ThreadSafe::Cache`. Use `Concurrent::Map` instead. + ([Pull Request](https://github.com/rails/rails/pull/21679)) + +### Deprecations + +* Deprecated `MissingSourceFile` in favor of `LoadError`. + ([commit](https://github.com/rails/rails/commit/734d97d2)) + +* Deprecated `alias_method_chain` in favour of `Module#prepend` introduced in + Ruby 2.0. + ([Pull Request](https://github.com/rails/rails/pull/19434)) + +* Deprecated `ActiveSupport::Concurrency::Latch` in favor of + `Concurrent::CountDownLatch` from concurrent-ruby. + ([Pull Request](https://github.com/rails/rails/pull/20866)) + +* Deprecated `:prefix` option of `number_to_human_size` with no replacement. + ([Pull Request](https://github.com/rails/rails/pull/21191)) + +* Deprecated `Module#qualified_const_` in favour of the builtin + `Module#const_` methods. + ([Pull Request](https://github.com/rails/rails/pull/17845)) + +* Deprecated passing string to define callback. + ([Pull Request](https://github.com/rails/rails/pull/22598)) + +* Deprecated `ActiveSupport::Cache::Store#namespaced_key`, + `ActiveSupport::Cache::MemCachedStore#escape_key`, and + `ActiveSupport::Cache::FileStore#key_file_path`. + Use `normalize_key` instead. + + Deprecated `ActiveSupport::Cache::LocaleCache#set_cache_value` in favor of `write_cache_value`. + ([Pull Request](https://github.com/rails/rails/pull/22215)) + +### Notable changes + +* Added `#verified` and `#valid_message?` methods to + `ActiveSupport::MessageVerifier`. + ([Pull Request](https://github.com/rails/rails/pull/17727)) + +* Changed the way in which callback chains can be halted. The preferred method + to halt a callback chain from now on is to explicitly `throw(:abort)`. + ([Pull Request](https://github.com/rails/rails/pull/17227)) + +* New config option + `config.active_support.halt_callback_chains_on_return_false` to specify + whether ActiveRecord, ActiveModel and ActiveModel::Validations callback + chains can be halted by returning `false` in a 'before' callback. + ([Pull Request](https://github.com/rails/rails/pull/17227)) + +* Changed the default test order from `:sorted` to `:random`. + ([commit](https://github.com/rails/rails/commit/5f777e4b5ee2e3e8e6fd0e2a208ec2a4d25a960d)) + +* Added `#on_weekend?`, `#next_weekday`, `#prev_weekday` methods to `Date`, + `Time`, and `DateTime`. + ([Pull Request](https://github.com/rails/rails/pull/18335)) + +* Added `same_time` option to `#next_week` and `#prev_week` for `Date`, `Time`, + and `DateTime`. + ([Pull Request](https://github.com/rails/rails/pull/18335)) + +* Added `#prev_day` and `#next_day` counterparts to `#yesterday` and + `#tomorrow` for `Date`, `Time`, and `DateTime`. + ([Pull Request](httpshttps://github.com/rails/rails/pull/18335)) + +* Added `SecureRandom.base58` for generation of random base58 strings. + ([commit](https://github.com/rails/rails/commit/b1093977110f18ae0cafe56c3d99fc22a7d54d1b)) + +* Added `file_fixture` to `ActiveSupport::TestCase`. + It provides a simple mechanism to access sample files in your test cases. + ([Pull Request](https://github.com/rails/rails/pull/18658)) + +* Added `#without` on `Enumerable` and `Array` to return a copy of an + enumerable without the specified elements. + ([Pull Request](https://github.com/rails/rails/pull/19157)) + +* Added `ActiveSupport::ArrayInquirer` and `Array#inquiry`. + ([Pull Request](https://github.com/rails/rails/pull/18939)) + +* Added `ActiveSupport::TimeZone#strptime` to allow parsing times as if + from a given timezone. + ([commit](https://github.com/rails/rails/commit/a5e507fa0b8180c3d97458a9b86c195e9857d8f6)) + +* Added `Integer#positive?` and `Integer#negative?` query methods + in the vein of `Fixnum#zero?`. + ([commit](https://github.com/rails/rails/commit/e54277a45da3c86fecdfa930663d7692fd083daa)) + +* Added a bang version to `ActiveSupport::OrderedOptions` get methods which will raise + an `KeyError` if the value is `.blank?`. + ([Pull Request](https://github.com/rails/rails/pull/20208)) + +* Added `Time.days_in_year` to return the number of days in the given year, or the + current year if no argument is provided. + ([commit](https://github.com/rails/rails/commit/2f4f4d2cf1e4c5a442459fc250daf66186d110fa)) + +* Added an evented file watcher to asynchronously detect changes in the + application source code, routes, locales, etc. + ([Pull Request](https://github.com/rails/rails/pull/22254)) + +* Added thread_m/cattr_accessor/reader/writer suite of methods for declaring + class and module variables that live per-thread. + ([Pull Request](https://github.com/rails/rails/pull/22630)) + + +Credits +------- + +See the +[full list of contributors to Rails](http://contributors.rubyonrails.org/) for +the many people who spent many hours making Rails, the stable and robust +framework it is. Kudos to all of them. + +[railties]: https://github.com/rails/rails/blob/5-0-stable/railties/CHANGELOG.md +[action-pack]: https://github.com/rails/rails/blob/5-0-stable/actionpack/CHANGELOG.md +[action-view]: https://github.com/rails/rails/blob/5-0-stable/actionview/CHANGELOG.md +[action-mailer]: https://github.com/rails/rails/blob/5-0-stable/actionmailer/CHANGELOG.md +[active-record]: https://github.com/rails/rails/blob/5-0-stable/activerecord/CHANGELOG.md +[active-model]: https://github.com/rails/rails/blob/5-0-stable/activemodel/CHANGELOG.md +[active-support]: https://github.com/rails/rails/blob/5-0-stable/activesupport/CHANGELOG.md +[active-job]: https://github.com/rails/rails/blob/5-0-stable/activejob/CHANGELOG.md diff --git a/guides/source/_welcome.html.erb b/guides/source/_welcome.html.erb index 67f5f1cdd5..f50bcddbe7 100644 --- a/guides/source/_welcome.html.erb +++ b/guides/source/_welcome.html.erb @@ -16,9 +16,9 @@ <% end %> <p> The guides for earlier releases: -<a href="http://guides.rubyonrails.org/v4.2.0/">Rails 4.2.0</a>, -<a href="http://guides.rubyonrails.org/v4.1.8/">Rails 4.1.8</a>, -<a href="http://guides.rubyonrails.org/v4.0.12/">Rails 4.0.12</a>, -<a href="http://guides.rubyonrails.org/v3.2.21/">Rails 3.2.21</a> and -<a href="http://guides.rubyonrails.org/v2.3.11/">Rails 2.3.11</a>. +<a href="http://guides.rubyonrails.org/v4.2/">Rails 4.2</a>, +<a href="http://guides.rubyonrails.org/v4.1/">Rails 4.1</a>, +<a href="http://guides.rubyonrails.org/v4.0/">Rails 4.0</a>, +<a href="http://guides.rubyonrails.org/v3.2/">Rails 3.2</a>, and +<a href="http://guides.rubyonrails.org/v2.3/">Rails 2.3</a>. </p> diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index 09fbdc0d32..9ef2c1a441 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -394,7 +394,7 @@ Rails sets up (for the CookieStore) a secret key used for signing the session da # Make sure the secret is at least 30 characters and all random, # no regular words or you'll be exposed to dictionary attacks. -# You can use `rake secret` to generate a secure secret key. +# You can use `rails secret` to generate a secure secret key. # Make sure the secrets in this file are kept private # if you're sharing your code publicly. @@ -810,7 +810,7 @@ The [Security Guide](security.html) has more about this and a lot of other secur The Request and Response Objects -------------------------------- -In every controller there are two accessor methods pointing to the request and the response objects associated with the request cycle that is currently in execution. The `request` method contains an instance of `AbstractRequest` and the `response` method returns a response object representing what is going to be sent back to the client. +In every controller there are two accessor methods pointing to the request and the response objects associated with the request cycle that is currently in execution. The `request` method contains an instance of `ActionDispatch::Request` and the `response` method returns a response object representing what is going to be sent back to the client. ### The `request` Object @@ -995,10 +995,6 @@ you would like in a response object. The `ActionController::Live` module allows you to create a persistent connection with a browser. Using this module, you will be able to send arbitrary data to the browser at specific points in time. -NOTE: The default Rails server (WEBrick) is a buffering web server and does not -support streaming. In order to use this feature, you'll need to use a non buffering -server like [Puma](http://puma.io), [Rainbows](http://rainbows.bogomips.org) -or [Passenger](https://www.phusionpassenger.com). #### Incorporating Live Streaming @@ -1029,7 +1025,7 @@ There are a couple of things to notice in the above example. We need to make sure to close the response stream. Forgetting to close the stream will leave the socket open forever. We also have to set the content type to `text/event-stream` before we write to the response stream. This is because headers cannot be written -after the response has been committed (when `response.committed` returns a truthy +after the response has been committed (when `response.committed?` returns a truthy value), which occurs when you `write` or `commit` the response stream. #### Example Usage @@ -1114,11 +1110,11 @@ Rescue Most likely your application is going to contain bugs or otherwise throw an exception that needs to be handled. For example, if the user follows a link to a resource that no longer exists in the database, Active Record will throw the `ActiveRecord::RecordNotFound` exception. -Rails' default exception handling displays a "500 Server Error" message for all exceptions. If the request was made locally, a nice traceback and some added information gets displayed so you can figure out what went wrong and deal with it. If the request was remote Rails will just display a simple "500 Server Error" message to the user, or a "404 Not Found" if there was a routing error or a record could not be found. Sometimes you might want to customize how these errors are caught and how they're displayed to the user. There are several levels of exception handling available in a Rails application: +Rails default exception handling displays a "500 Server Error" message for all exceptions. If the request was made locally, a nice traceback and some added information gets displayed so you can figure out what went wrong and deal with it. If the request was remote Rails will just display a simple "500 Server Error" message to the user, or a "404 Not Found" if there was a routing error or a record could not be found. Sometimes you might want to customize how these errors are caught and how they're displayed to the user. There are several levels of exception handling available in a Rails application: ### The Default 500 and 404 Templates -By default a production application will render either a 404 or a 500 error message. These messages are contained in static HTML files in the `public` folder, in `404.html` and `500.html` respectively. You can customize these files to add some extra information and layout, but remember that they are static; i.e. you can't use RHTML or layouts in them, just plain HTML. +By default a production application will render either a 404 or a 500 error message, in the development environment all unhandled exceptions are raised. These messages are contained in static HTML files in the `public` folder, in `404.html` and `500.html` respectively. You can customize these files to add some extra information and style, but remember that they are static HTML; i.e. you can't use ERB, SCSS, CoffeeScript, or layouts for them. ### `rescue_from` @@ -1150,7 +1146,7 @@ class ApplicationController < ActionController::Base def user_not_authorized flash[:error] = "You don't have access to this section." - redirect_to :back + redirect_back(fallback_location: root_path) end end @@ -1174,7 +1170,11 @@ end WARNING: You shouldn't do `rescue_from Exception` or `rescue_from StandardError` unless you have a particular reason as it will cause serious side-effects (e.g. you won't be able to see exception details and tracebacks during development). -NOTE: Certain exceptions are only rescuable from the `ApplicationController` class, as they are raised before the controller gets initialized and the action gets executed. See Pratik Naik's [article](http://m.onkey.org/2008/7/20/rescue-from-dispatching) on the subject for more information. +NOTE: When running in the production environment, all +`ActiveRecord::RecordNotFound` errors render the 404 error page. Unless you need +a custom behavior you don't need to handle this. + +NOTE: Certain exceptions are only rescuable from the `ApplicationController` class, as they are raised before the controller gets initialized and the action gets executed. Force HTTPS protocol -------------------- diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md index c31b50fcfc..cd2c13e8c1 100644 --- a/guides/source/action_mailer_basics.md +++ b/guides/source/action_mailer_basics.md @@ -170,7 +170,7 @@ First, let's create a simple `User` scaffold: ```bash $ bin/rails generate scaffold user name email login -$ bin/rake db:migrate +$ bin/rails db:migrate ``` Now that we have a user model to play with, we will just edit the @@ -326,7 +326,7 @@ key. The list of emails can be an array of email addresses or a single string with the addresses separated by commas. ```ruby -class AdminMailer < ActionMailer::Base +class AdminMailer < ApplicationMailer default to: Proc.new { Admin.pluck(:email) }, from: 'notification@example.com' @@ -759,6 +759,9 @@ config.action_mailer.smtp_settings = { authentication: 'plain', enable_starttls_auto: true } ``` +Note: As of July 15, 2014, Google increased [its security measures](https://support.google.com/accounts/answer/6010255) and now blocks attempts from apps it deems less secure. +You can change your gmail settings [here](https://www.google.com/settings/security/lesssecureapps) to allow the attempts or +use another ESP to send email by replacing 'smtp.gmail.com' above with the address of your provider. Mailer Testing -------------- diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md index 98c6cbd540..543937f8e5 100644 --- a/guides/source/action_view_overview.md +++ b/guides/source/action_view_overview.md @@ -15,7 +15,7 @@ After reading this guide, you will know: What is Action View? -------------------- -Action View and Action Controller are the two major components of Action Pack. In Rails, web requests are handled by Action Pack, which splits the work into a controller part (performing the logic) and a view part (rendering a template). Typically, Action Controller will be concerned with communicating with the database and performing CRUD actions where necessary. Action View is then responsible for compiling the response. +In Rails, web requests are handled by [Action Controller](action_controller_overview.html) and Action View. Typically, Action Controller will be concerned with communicating with the database and performing CRUD actions where necessary. Action View is then responsible for compiling the response. Action View templates are written using embedded Ruby in tags mingled with HTML. To avoid cluttering the templates with boilerplate code, a number of helper classes provide common behavior for forms, dates, and strings. It's also easy to add new helpers to your application as it evolves. @@ -147,6 +147,39 @@ xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do end ``` +#### Jbuilder +[Jbuilder](https://github.com/rails/jbuilder) is a gem that's +maintained by the Rails team and included in the default Rails Gemfile. +It's similar to Builder, but is used to generate JSON, instead of XML. + +If you don't have it, you can add the following to your Gemfile: + +```ruby +gem 'jbuilder' +``` + +A Jbuilder object named `json` is automatically made available to templates with +a `.jbuilder` extension. + +Here is a basic example: + +```ruby +json.name("Alex") +json.email("alex@example.com") +``` + +would produce: + +```json +{ + "name": "Alex", + "email: "alex@example.com" +} +``` + +See the [Jbuilder documention](https://github.com/rails/jbuilder#jbuilder) for +more examples and information. + #### Template Caching By default, Rails will compile each template to a method in order to render it. When you alter a template, Rails will check the file's modification time and recompile it in development mode. @@ -566,7 +599,7 @@ This would add something like "Process data files (0.34523)" to the log, which y #### cache -A method for caching fragments of a view rather than an entire action or page. This technique is useful caching pieces like menus, lists of news topics, static HTML fragments, and so on. This method takes a block that contains the content you wish to cache. See `ActionController::Caching::Fragments` for more information. +A method for caching fragments of a view rather than an entire action or page. This technique is useful for caching pieces like menus, lists of news topics, static HTML fragments, and so on. This method takes a block that contains the content you wish to cache. See `ActionController::Caching::Fragments` for more information. ```erb <% cache do %> @@ -957,11 +990,11 @@ Returns `select` and `option` tags for the collection of existing return values Example object structure for use with this method: ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord belongs_to :author end -class Author < ActiveRecord::Base +class Author < ApplicationRecord has_many :articles def name_with_initial "#{first_name.first}. #{last_name}" @@ -993,11 +1026,11 @@ Returns `radio_button` tags for the collection of existing return values of `met Example object structure for use with this method: ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord belongs_to :author end -class Author < ActiveRecord::Base +class Author < ApplicationRecord has_many :articles def name_with_initial "#{first_name.first}. #{last_name}" @@ -1029,11 +1062,11 @@ Returns `check_box` tags for the collection of existing return values of `method Example object structure for use with this method: ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord has_and_belongs_to_many :authors end -class Author < ActiveRecord::Base +class Author < ApplicationRecord has_and_belongs_to_many :articles def name_with_initial "#{first_name.first}. #{last_name}" @@ -1066,12 +1099,12 @@ Returns a string of `option` tags, like `options_from_collection_for_select`, bu Example object structure for use with this method: ```ruby -class Continent < ActiveRecord::Base +class Continent < ApplicationRecord has_many :countries # attribs: id, name end -class Country < ActiveRecord::Base +class Country < ApplicationRecord belongs_to :continent # attribs: id, name, continent_id end @@ -1214,7 +1247,7 @@ file_field_tag 'attachment' #### form_tag -Starts a form tag that points the action to an url configured with `url_for_options` just like `ActionController::Base#url_for`. +Starts a form tag that points the action to a url configured with `url_for_options` just like `ActionController::Base#url_for`. ```html+erb <%= form_tag '/articles' do %> @@ -1443,12 +1476,12 @@ Sanitizes a block of CSS code. Strips all link tags from text leaving just the link text. ```ruby -strip_links("<a href="http://rubyonrails.org">Ruby on Rails</a>") +strip_links('<a href="http://rubyonrails.org">Ruby on Rails</a>') # => Ruby on Rails ``` ```ruby -strip_links("emails to <a href="mailto:me@email.com">me@email.com</a>.") +strip_links('emails to <a href="mailto:me@email.com">me@email.com</a>.') # => emails to me@email.com. ``` diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md index dd545b56f5..76c13f0ea9 100644 --- a/guides/source/active_job_basics.md +++ b/guides/source/active_job_basics.md @@ -70,34 +70,41 @@ Here's what a job looks like: class GuestsCleanupJob < ActiveJob::Base queue_as :default - def perform(*args) + def perform(*guests) # Do something later end end ``` +Note that you can define `perform` with as many arguments as you want. + ### Enqueue the Job Enqueue a job like so: ```ruby -# Enqueue a job to be performed as soon the queuing system is +# Enqueue a job to be performed as soon as the queuing system is # free. -MyJob.perform_later record +GuestsCleanupJob.perform_later guest ``` ```ruby # Enqueue a job to be performed tomorrow at noon. -MyJob.set(wait_until: Date.tomorrow.noon).perform_later(record) +GuestsCleanupJob.set(wait_until: Date.tomorrow.noon).perform_later(guest) ``` ```ruby # Enqueue a job to be performed 1 week from now. -MyJob.set(wait: 1.week).perform_later(record) +GuestsCleanupJob.set(wait: 1.week).perform_later(guest) ``` -That's it! +```ruby +# `perform_now` and `perform_later` will call `perform` under the hood so +# you can pass as many arguments as defined in the latter. +GuestsCleanupJob.perform_later(guest1, guest2, filter: 'some_filter') +``` +That's it! Job Execution ------------- @@ -129,10 +136,19 @@ module YourApp end ``` -NOTE: Since jobs run in parallel to your Rails application, most queuing libraries +### Starting the Backend + +Since jobs run in parallel to your Rails application, most queuing libraries require that you start a library-specific queuing service (in addition to -starting your Rails app) for the job processing to work. For information on -how to do that refer to the documentation of your respective library. +starting your Rails app) for the job processing to work. Refer to library +documentation for instructions on starting your queue backend. + +Here is a noncomprehensive list of documentation: + +- [Sidekiq](https://github.com/mperham/sidekiq/wiki/Active-Job) +- [Resque](https://github.com/resque/resque/wiki/ActiveJob) +- [Sucker Punch](https://github.com/brandonhilkert/sucker_punch#active-job) +- [Queue Classic](https://github.com/QueueClassic/queue_classic#active-job) Queues ------ @@ -158,7 +174,7 @@ module YourApp end end -# app/jobs/guests_cleanup.rb +# app/jobs/guests_cleanup_job.rb class GuestsCleanupJob < ActiveJob::Base queue_as :low_priority #.... @@ -181,7 +197,7 @@ module YourApp end end -# app/jobs/guests_cleanup.rb +# app/jobs/guests_cleanup_job.rb class GuestsCleanupJob < ActiveJob::Base queue_as :low_priority #.... @@ -280,6 +296,19 @@ UserMailer.welcome(@user).deliver_later ``` +Internationalization +-------------------- + +Each job uses the `I18n.locale` set when the job was created. Useful if you send +emails asynchronously: + +```ruby +I18n.locale = :eo + +UserMailer.welcome(@user).deliver_later # Email will be localized to Esperanto. +``` + + GlobalID -------- diff --git a/guides/source/active_model_basics.md b/guides/source/active_model_basics.md index 4b2bfaee2f..c05e20aceb 100644 --- a/guides/source/active_model_basics.md +++ b/guides/source/active_model_basics.md @@ -8,12 +8,12 @@ classes. Active Model allows for Action Pack helpers to interact with plain Ruby objects. Active Model also helps build custom ORMs for use outside of the Rails framework. -After reading this guide, you will be able to add to plain Ruby objects: +After reading this guide, you will know: -* The ability to behave like an Active Record model. -* Callbacks and validations like Active Record. -* Serializers. -* Integration with the Rails internationalization (i18n) framework. +* How an Active Record model behaves. +* How Callbacks and validations work. +* How serializers work. +* The Rails internationalization (i18n) framework. -------------------------------------------------------------------------------- @@ -156,7 +156,7 @@ person.changed? # => false person.first_name = "First Name" person.first_name # => "First Name" -# returns if any attribute has changed. +# returns true if any of the attributes have unsaved changes, false otherwise. person.changed? # => true # returns a list of attributes that have changed before saving. @@ -197,7 +197,7 @@ person.last_name_change # => nil ### Validations -`ActiveModel::Validations` module adds the ability to validate class objects +The `ActiveModel::Validations` module adds the ability to validate class objects like in Active Record. ```ruby @@ -292,7 +292,7 @@ objects. ### Serialization -`ActiveModel::Serialization` provides a basic serialization for your object. +`ActiveModel::Serialization` provides basic serialization for your object. You need to declare an attributes hash which contains the attributes you want to serialize. Attributes must be strings, not symbols. @@ -319,9 +319,8 @@ person.serializable_hash # => {"name"=>"Bob"} #### ActiveModel::Serializers -Rails provides two serializers `ActiveModel::Serializers::JSON` and -`ActiveModel::Serializers::Xml`. Both of these modules automatically include -the `ActiveModel::Serialization`. +Rails provides a `ActiveModel::Serializers::JSON` serializer. +This module automatically include the `ActiveModel::Serialization`. ##### ActiveModel::Serializers::JSON @@ -340,7 +339,7 @@ class Person end ``` -With the `as_json` you have a hash representing the model. +With the `as_json` method you have a hash representing the model. ```ruby person = Person.new @@ -379,62 +378,6 @@ person.from_json(json) # => #<Person:0x00000100c773f0 @name="Bob"> person.name # => "Bob" ``` -##### ActiveModel::Serializers::Xml - -To use the `ActiveModel::Serializers::Xml` you only need to change from -`ActiveModel::Serialization` to `ActiveModel::Serializers::Xml`. - -```ruby -class Person - include ActiveModel::Serializers::Xml - - attr_accessor :name - - def attributes - {'name' => nil} - end -end -``` - -With the `to_xml` you have an XML representing the model. - -```ruby -person = Person.new -person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<person>\n <name nil=\"true\"/>\n</person>\n" -person.name = "Bob" -person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<person>\n <name>Bob</name>\n</person>\n" -``` - -From an XML string you define the attributes of the model. -You need to have the `attributes=` method defined on your class: - -```ruby -class Person - include ActiveModel::Serializers::Xml - - attr_accessor :name - - def attributes=(hash) - hash.each do |key, value| - send("#{key}=", value) - end - end - - def attributes - {'name' => nil} - end -end -``` - -Now it is possible to create an instance of person and set the attributes using `from_xml`. - -```ruby -xml = { name: 'Bob' }.to_xml -person = Person.new -person.from_xml(xml) # => #<Person:0x00000100c773f0 @name="Bob"> -person.name # => "Bob" -``` - ### Translation `ActiveModel::Translation` provides integration between your object and the Rails @@ -465,7 +408,7 @@ Person.human_attribute_name('name') # => "Nome" ### Lint Tests -`ActiveModel::Lint::Tests` allow you to test whether an object is compliant with +`ActiveModel::Lint::Tests` allows you to test whether an object is compliant with the Active Model API. * app/models/person.rb @@ -485,14 +428,14 @@ the Active Model API. class PersonTest < ActiveSupport::TestCase include ActiveModel::Lint::Tests - def setup + setup do @model = Person.new end end ``` ```bash -$ rake test +$ rails test Run options: --seed 14596 @@ -518,14 +461,14 @@ an accessor named `password` with certain validations on it. #### Requirements -`ActiveModel::SecurePassword` depends on the [`bcrypt`](https://github.com/codahale/bcrypt-ruby 'BCrypt'), +`ActiveModel::SecurePassword` depends on [`bcrypt`](https://github.com/codahale/bcrypt-ruby 'BCrypt'), so include this gem in your Gemfile to use `ActiveModel::SecurePassword` correctly. In order to make this work, the model must have an accessor named `password_digest`. The `has_secure_password` will add the following validations on the `password` accessor: 1. Password should be present. 2. Password should be equal to its confirmation. -3. This maximum length of a password is 72 (required by `bcrypt` on which ActiveModel::SecurePassword depends) +3. The maximum length of a password is 72 (required by `bcrypt` on which ActiveModel::SecurePassword depends) #### Examples @@ -546,7 +489,7 @@ person.password = 'aditya' person.password_confirmation = 'nomatch' person.valid? # => false -# When the length of password, exceeds 72. +# When the length of password exceeds 72. person.password = person.password_confirmation = 'a' * 100 person.valid? # => false diff --git a/guides/source/active_record_basics.md b/guides/source/active_record_basics.md index a227b54040..fba89f9d13 100644 --- a/guides/source/active_record_basics.md +++ b/guides/source/active_record_basics.md @@ -132,17 +132,17 @@ Creating Active Record Models ----------------------------- It is very easy to create Active Record models. All you have to do is to -subclass the `ActiveRecord::Base` class and you're good to go: +subclass the `ApplicationRecord` class and you're good to go: ```ruby -class Product < ActiveRecord::Base +class Product < ApplicationRecord end ``` This will create a `Product` model, mapped to a `products` table at the database. By doing this you'll also have the ability to map the columns of each row in that table with the attributes of the instances of your model. Suppose -that the `products` table was created using an SQL sentence like: +that the `products` table was created using an SQL statement like: ```sql CREATE TABLE products ( @@ -168,11 +168,12 @@ What if you need to follow a different naming convention or need to use your Rails application with a legacy database? No problem, you can easily override the default conventions. -You can use the `ActiveRecord::Base.table_name=` method to specify the table -name that should be used: +`ApplicationRecord` inherits from `ActiveRecord::Base`, which defines a +number of helpful methods. You can use the `ActiveRecord::Base.table_name=` +method to specify the table name that should be used: ```ruby -class Product < ActiveRecord::Base +class Product < ApplicationRecord self.table_name = "my_products" end ``` @@ -193,7 +194,7 @@ It's also possible to override the column that should be used as the table's primary key using the `ActiveRecord::Base.primary_key=` method: ```ruby -class Product < ActiveRecord::Base +class Product < ApplicationRecord self.primary_key = "product_id" end ``` @@ -260,7 +261,7 @@ david = User.find_by(name: 'David') ```ruby # find all users named David who are Code Artists and sort by created_at in reverse chronological order -users = User.where(name: 'David', occupation: 'Code Artist').order('created_at DESC') +users = User.where(name: 'David', occupation: 'Code Artist').order(created_at: :desc) ``` You can learn more about querying an Active Record model in the [Active Record @@ -320,7 +321,7 @@ they raise the exception `ActiveRecord::RecordInvalid` if validation fails. A quick example to illustrate: ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord validates :name, presence: true end @@ -350,7 +351,7 @@ database that Active Record supports using `rake`. Here's a migration that creates a table: ```ruby -class CreatePublications < ActiveRecord::Migration +class CreatePublications < ActiveRecord::Migration[5.0] def change create_table :publications do |t| t.string :title @@ -368,8 +369,8 @@ end ``` Rails keeps track of which files have been committed to the database and -provides rollback features. To actually create the table, you'd run `rake db:migrate` -and to roll it back, `rake db:rollback`. +provides rollback features. To actually create the table, you'd run `rails db:migrate` +and to roll it back, `rails db:rollback`. Note that the above code is database-agnostic: it will run in MySQL, PostgreSQL, Oracle and others. You can learn more about migrations in the diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md index 13989a3b33..fb5d2065d3 100644 --- a/guides/source/active_record_callbacks.md +++ b/guides/source/active_record_callbacks.md @@ -31,7 +31,7 @@ Callbacks are methods that get called at certain moments of an object's life cyc In order to use the available callbacks, you need to register them. You can implement the callbacks as ordinary methods and use a macro-style class method to register them as callbacks: ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord validates :login, :email, presence: true before_validation :ensure_login_has_a_value @@ -48,7 +48,7 @@ end The macro-style class methods can also receive a block. Consider using this style if the code inside your block is so short that it fits in a single line: ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord validates :login, :email, presence: true before_create do @@ -60,7 +60,7 @@ end Callbacks can also be registered to only fire on certain life cycle events: ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord before_validation :normalize_name, on: :create # :on takes an array as well @@ -126,7 +126,7 @@ The `after_find` callback will be called whenever Active Record loads a record f The `after_initialize` and `after_find` callbacks have no `before_*` counterparts, but they can be registered just like the other Active Record callbacks. ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord after_initialize do |user| puts "You have initialized an object!" end @@ -151,7 +151,7 @@ You have initialized an object! The `after_touch` callback will be called whenever an Active Record object is touched. ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord after_touch do |user| puts "You have touched an object" end @@ -168,14 +168,14 @@ You have touched an object It can be used along with `belongs_to`: ```ruby -class Employee < ActiveRecord::Base +class Employee < ApplicationRecord belongs_to :company, touch: true after_touch do puts 'An Employee was touched' end end -class Company < ActiveRecord::Base +class Company < ApplicationRecord has_many :employees after_touch :log_when_employees_or_company_touched @@ -258,7 +258,7 @@ As you start registering new callbacks for your models, they will be queued for The whole callback chain is wrapped in a transaction. If any _before_ callback method returns exactly `false` or raises an exception, the execution chain gets halted and a ROLLBACK is issued; _after_ callbacks can only accomplish that by raising an exception. -WARNING. Any exception that is not `ActiveRecord::Rollback` will be re-raised by Rails after the callback chain is halted. Raising an exception other than `ActiveRecord::Rollback` may break code that does not expect methods like `save` and `update_attributes` (which normally try to return `true` or `false`) to raise an exception. +WARNING. Any exception that is not `ActiveRecord::Rollback` or `ActiveRecord::RecordInvalid` will be re-raised by Rails after the callback chain is halted. Raising an exception other than `ActiveRecord::Rollback` or `ActiveRecord::RecordInvalid` may break code that does not expect methods like `save` and `update_attributes` (which normally try to return `true` or `false`) to raise an exception. Relational Callbacks -------------------- @@ -266,11 +266,11 @@ Relational Callbacks Callbacks work through model relationships, and can even be defined by them. Suppose an example where a user has many articles. A user's articles should be destroyed if the user is destroyed. Let's add an `after_destroy` callback to the `User` model by way of its relationship to the `Article` model: ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord has_many :articles, dependent: :destroy end -class Article < ActiveRecord::Base +class Article < ApplicationRecord after_destroy :log_destroy_action def log_destroy_action @@ -297,7 +297,7 @@ As with validations, we can also make the calling of a callback method condition You can associate the `:if` and `:unless` options with a symbol corresponding to the name of a predicate method that will get called right before the callback. When using the `:if` option, the callback won't be executed if the predicate method returns false; when using the `:unless` option, the callback won't be executed if the predicate method returns true. This is the most common option. Using this form of registration it is also possible to register several different predicates that should be called to check if the callback should be executed. ```ruby -class Order < ActiveRecord::Base +class Order < ApplicationRecord before_save :normalize_card_number, if: :paid_with_card? end ``` @@ -307,7 +307,7 @@ end You can also use a string that will be evaluated using `eval` and hence needs to contain valid Ruby code. You should use this option only when the string represents a really short condition: ```ruby -class Order < ActiveRecord::Base +class Order < ApplicationRecord before_save :normalize_card_number, if: "paid_with_card?" end ``` @@ -317,7 +317,7 @@ end Finally, it is possible to associate `:if` and `:unless` with a `Proc` object. This option is best suited when writing short validation methods, usually one-liners: ```ruby -class Order < ActiveRecord::Base +class Order < ApplicationRecord before_save :normalize_card_number, if: Proc.new { |order| order.paid_with_card? } end @@ -328,7 +328,7 @@ end When writing conditional callbacks, it is possible to mix both `:if` and `:unless` in the same callback declaration: ```ruby -class Comment < ActiveRecord::Base +class Comment < ApplicationRecord after_create :send_email_to_author, if: :author_wants_emails?, unless: Proc.new { |comment| comment.article.ignore_comments? } end @@ -354,7 +354,7 @@ end When declared inside a class, as above, the callback methods will receive the model object as a parameter. We can now use the callback class in the model: ```ruby -class PictureFile < ActiveRecord::Base +class PictureFile < ApplicationRecord after_destroy PictureFileCallbacks.new end ``` @@ -374,7 +374,7 @@ end If the callback method is declared this way, it won't be necessary to instantiate a `PictureFileCallbacks` object. ```ruby -class PictureFile < ActiveRecord::Base +class PictureFile < ApplicationRecord after_destroy PictureFileCallbacks end ``` @@ -398,7 +398,7 @@ end By using the `after_commit` callback we can account for this case. ```ruby -class PictureFile < ActiveRecord::Base +class PictureFile < ApplicationRecord after_commit :delete_picture_file_from_disk, on: [:destroy] def delete_picture_file_from_disk @@ -412,4 +412,23 @@ end NOTE: the `:on` option specifies when a callback will be fired. If you don't supply the `:on` option the callback will fire for every action. +Since using `after_commit` callback only on create, update or delete is +common, there are aliases for those operations: + +* `after_create_commit` +* `after_update_commit` +* `after_destroy_commit` + +```ruby +class PictureFile < ApplicationRecord + after_destroy_commit :delete_picture_file_from_disk + + def delete_picture_file_from_disk + if File.exist?(filepath) + File.delete(filepath) + end + end +end +``` + WARNING. The `after_commit` and `after_rollback` callbacks are guaranteed to be called for all models created, updated, or destroyed within a transaction block. If any exceptions are raised within one of these callbacks, they will be ignored so that they don't interfere with the other callbacks. As such, if your callback code could raise an exception, you'll need to rescue it and handle it appropriately within the callback. diff --git a/guides/source/active_record_migrations.md b/guides/source/active_record_migrations.md index ce605c912e..83f4b951ee 100644 --- a/guides/source/active_record_migrations.md +++ b/guides/source/active_record_migrations.md @@ -12,7 +12,7 @@ After reading this guide, you will know: * The generators you can use to create them. * The methods Active Record provides to manipulate your database. -* The Rake tasks that manipulate migrations and your schema. +* The bin/rails tasks that manipulate migrations and your schema. * How migrations relate to `schema.rb`. -------------------------------------------------------------------------------- @@ -35,7 +35,7 @@ history to the latest version. Active Record will also update your Here's an example of a migration: ```ruby -class CreateProducts < ActiveRecord::Migration +class CreateProducts < ActiveRecord::Migration[5.0] def change create_table :products do |t| t.string :name @@ -72,7 +72,7 @@ If you wish for a migration to do something that Active Record doesn't know how to reverse, you can use `reversible`: ```ruby -class ChangeProductsPrice < ActiveRecord::Migration +class ChangeProductsPrice < ActiveRecord::Migration[5.0] def change reversible do |dir| change_table :products do |t| @@ -87,7 +87,7 @@ end Alternatively, you can use `up` and `down` instead of `change`: ```ruby -class ChangeProductsPrice < ActiveRecord::Migration +class ChangeProductsPrice < ActiveRecord::Migration[5.0] def up change_table :products do |t| t.change :price, :string @@ -129,7 +129,7 @@ $ bin/rails generate migration AddPartNumberToProducts This will create an empty but appropriately named migration: ```ruby -class AddPartNumberToProducts < ActiveRecord::Migration +class AddPartNumberToProducts < ActiveRecord::Migration[5.0] def change end end @@ -146,7 +146,7 @@ $ bin/rails generate migration AddPartNumberToProducts part_number:string will generate ```ruby -class AddPartNumberToProducts < ActiveRecord::Migration +class AddPartNumberToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :part_number, :string end @@ -162,7 +162,7 @@ $ bin/rails generate migration AddPartNumberToProducts part_number:string:index will generate ```ruby -class AddPartNumberToProducts < ActiveRecord::Migration +class AddPartNumberToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :part_number, :string add_index :products, :part_number @@ -180,7 +180,7 @@ $ bin/rails generate migration RemovePartNumberFromProducts part_number:string generates ```ruby -class RemovePartNumberFromProducts < ActiveRecord::Migration +class RemovePartNumberFromProducts < ActiveRecord::Migration[5.0] def change remove_column :products, :part_number, :string end @@ -196,7 +196,7 @@ $ bin/rails generate migration AddDetailsToProducts part_number:string price:dec generates ```ruby -class AddDetailsToProducts < ActiveRecord::Migration +class AddDetailsToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :part_number, :string add_column :products, :price, :decimal @@ -215,7 +215,7 @@ $ bin/rails generate migration CreateProducts name:string part_number:string generates ```ruby -class CreateProducts < ActiveRecord::Migration +class CreateProducts < ActiveRecord::Migration[5.0] def change create_table :products do |t| t.string :name @@ -239,7 +239,7 @@ $ bin/rails generate migration AddUserRefToProducts user:references generates ```ruby -class AddUserRefToProducts < ActiveRecord::Migration +class AddUserRefToProducts < ActiveRecord::Migration[5.0] def change add_reference :products, :user, index: true, foreign_key: true end @@ -257,7 +257,7 @@ $ bin/rails g migration CreateJoinTableCustomerProduct customer product will produce the following migration: ```ruby -class CreateJoinTableCustomerProduct < ActiveRecord::Migration +class CreateJoinTableCustomerProduct < ActiveRecord::Migration[5.0] def change create_join_table :customers, :products do |t| # t.index [:customer_id, :product_id] @@ -281,7 +281,7 @@ $ bin/rails generate model Product name:string description:text will create a migration that looks like this ```ruby -class CreateProducts < ActiveRecord::Migration +class CreateProducts < ActiveRecord::Migration[5.0] def change create_table :products do |t| t.string :name @@ -309,7 +309,7 @@ $ bin/rails generate migration AddDetailsToProducts 'price:decimal{5,2}' supplie will produce a migration that looks like this ```ruby -class AddDetailsToProducts < ActiveRecord::Migration +class AddDetailsToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :price, :decimal, precision: 5, scale: 2 add_reference :products, :supplier, polymorphic: true, index: true @@ -357,8 +357,8 @@ will append `ENGINE=BLACKHOLE` to the SQL statement used to create the table ### Creating a Join Table -Migration method `create_join_table` creates an HABTM join table. A typical use -would be: +The migration method `create_join_table` creates an HABTM (has and belongs to +many) join table. A typical use would be: ```ruby create_join_table :products, :categories @@ -367,23 +367,21 @@ create_join_table :products, :categories which creates a `categories_products` table with two columns called `category_id` and `product_id`. These columns have the option `:null` set to `false` by default. This can be overridden by specifying the `:column_options` -option. +option: ```ruby -create_join_table :products, :categories, column_options: {null: true} +create_join_table :products, :categories, column_options: { null: true } ``` -will create the `product_id` and `category_id` with the `:null` option as -`true`. - -You can pass the option `:table_name` when you want to customize the table -name. For example: +By default, the name of the join table comes from the union of the first two +arguments provided to create_join_table, in alphabetical order. +To customize the name of the table, provide a `:table_name` option: ```ruby create_join_table :products, :categories, table_name: :categorization ``` -will create a `categorization` table. +creates a `categorization` table. `create_join_table` also accepts a block, which you can use to add indices (which are not created by default) or additional columns: @@ -456,8 +454,6 @@ number of digits after the decimal point. are using a dynamic value (such as a date), the default will only be calculated the first time (i.e. on the date the migration is applied). * `index` Adds an index for the column. -* `required` Adds `required: true` for `belongs_to` associations and -`null: false` to the column in the migration. Some adapters may support additional options; see the adapter specific API docs for further information. @@ -524,20 +520,27 @@ majority of cases, where Active Record knows how to reverse the migration automatically. Currently, the `change` method supports only these migration definitions: -* `add_column` -* `add_index` -* `add_reference` -* `add_timestamps` -* `add_foreign_key` -* `create_table` -* `create_join_table` -* `drop_table` (must supply a block) -* `drop_join_table` (must supply a block) -* `remove_timestamps` -* `rename_column` -* `rename_index` -* `remove_reference` -* `rename_table` +* add_column +* add_foreign_key +* add_index +* add_reference +* add_timestamps +* change_column_default (must supply a :from and :to option) +* change_column_null +* create_join_table +* create_table +* disable_extension +* drop_join_table +* drop_table (must supply a block) +* enable_extension +* remove_column (must supply a type) +* remove_foreign_key (must supply a second table) +* remove_index +* remove_reference +* remove_timestamps +* rename_column +* rename_index +* rename_table `change_table` is also reversible, as long as the block does not call `change`, `change_default` or `remove`. @@ -560,7 +563,7 @@ to reverse. You can use `reversible` to specify what to do when running a migration and what else to do when reverting it. For example: ```ruby -class ExampleMigration < ActiveRecord::Migration +class ExampleMigration < ActiveRecord::Migration[5.0] def change create_table :distributors do |t| t.string :zipcode @@ -613,7 +616,7 @@ is wise to perform the transformations in precisely the reverse order they were made in the `up` method. The example in the `reversible` section is equivalent to: ```ruby -class ExampleMigration < ActiveRecord::Migration +class ExampleMigration < ActiveRecord::Migration[5.0] def up create_table :distributors do |t| t.string :zipcode @@ -654,9 +657,9 @@ can't be done. You can use Active Record's ability to rollback migrations using the `revert` method: ```ruby -require_relative '2012121212_example_migration' +require_relative '20121212123456_example_migration' -class FixupExampleMigration < ActiveRecord::Migration +class FixupExampleMigration < ActiveRecord::Migration[5.0] def change revert ExampleMigration @@ -674,7 +677,7 @@ is later decided it would be best to use Active Record validations, in place of the `CHECK` constraint, to verify the zipcode. ```ruby -class DontUseConstraintForZipcodeValidationMigration < ActiveRecord::Migration +class DontUseConstraintForZipcodeValidationMigration < ActiveRecord::Migration[5.0] def change revert do # copy-pasted code from ExampleMigration @@ -714,10 +717,10 @@ you will have to use `structure.sql` as dump method. See Running Migrations ------------------ -Rails provides a set of Rake tasks to run certain sets of migrations. +Rails provides a set of bin/rails tasks to run certain sets of migrations. -The very first migration related Rake task you will use will probably be -`rake db:migrate`. In its most basic form it just runs the `change` or `up` +The very first migration related bin/rails task you will use will probably be +`rails db:migrate`. In its most basic form it just runs the `change` or `up` method for all the migrations that have not yet been run. If there are no such migrations, it exits. It will run these migrations in order based on the date of the migration. @@ -731,7 +734,7 @@ is the numerical prefix on the migration's filename. For example, to migrate to version 20080906120000 run: ```bash -$ bin/rake db:migrate VERSION=20080906120000 +$ bin/rails db:migrate VERSION=20080906120000 ``` If version 20080906120000 is greater than the current version (i.e., it is @@ -748,7 +751,7 @@ mistake in it and wish to correct it. Rather than tracking down the version number associated with the previous migration you can run: ```bash -$ bin/rake db:rollback +$ bin/rails db:rollback ``` This will rollback the latest migration, either by reverting the `change` @@ -756,7 +759,7 @@ method or by running the `down` method. If you need to undo several migrations you can provide a `STEP` parameter: ```bash -$ bin/rake db:rollback STEP=3 +$ bin/rails db:rollback STEP=3 ``` will revert the last 3 migrations. @@ -766,26 +769,26 @@ back up again. As with the `db:rollback` task, you can use the `STEP` parameter if you need to go more than one version back, for example: ```bash -$ bin/rake db:migrate:redo STEP=3 +$ bin/rails db:migrate:redo STEP=3 ``` -Neither of these Rake tasks do anything you could not do with `db:migrate`. They +Neither of these bin/rails tasks do anything you could not do with `db:migrate`. They are simply more convenient, since you do not need to explicitly specify the version to migrate to. ### Setup the Database -The `rake db:setup` task will create the database, load the schema and initialize +The `rails db:setup` task will create the database, load the schema and initialize it with the seed data. ### Resetting the Database -The `rake db:reset` task will drop the database and set it up again. This is -functionally equivalent to `rake db:drop db:setup`. +The `rails db:reset` task will drop the database and set it up again. This is +functionally equivalent to `rails db:drop db:setup`. NOTE: This is not the same as running all the migrations. It will only use the -contents of the current `schema.rb` file. If a migration can't be rolled back, -`rake db:reset` may not help you. To find out more about dumping the schema see +contents of the current `db/schema.rb` or `db/structure.sql` file. If a migration can't be rolled back, +`rails db:reset` may not help you. To find out more about dumping the schema see [Schema Dumping and You](#schema-dumping-and-you) section. ### Running Specific Migrations @@ -796,7 +799,7 @@ the corresponding migration will have its `change`, `up` or `down` method invoked, for example: ```bash -$ bin/rake db:migrate:up VERSION=20080906120000 +$ bin/rails db:migrate:up VERSION=20080906120000 ``` will run the 20080906120000 migration by running the `change` method (or the @@ -806,13 +809,13 @@ Active Record believes that it has already been run. ### Running Migrations in Different Environments -By default running `rake db:migrate` will run in the `development` environment. +By default running `bin/rails db:migrate` will run in the `development` environment. To run migrations against another environment you can specify it using the `RAILS_ENV` environment variable while running the command. For example to run migrations against the `test` environment you could run: ```bash -$ bin/rake db:migrate RAILS_ENV=test +$ bin/rails db:migrate RAILS_ENV=test ``` ### Changing the Output of Running Migrations @@ -838,7 +841,7 @@ Several methods are provided in migrations that allow you to control all this: For example, this migration: ```ruby -class CreateProducts < ActiveRecord::Migration +class CreateProducts < ActiveRecord::Migration[5.0] def change suppress_messages do create_table :products do |t| @@ -873,7 +876,7 @@ generates the following output == CreateProducts: migrated (10.0054s) ======================================= ``` -If you want Active Record to not output anything, then running `rake db:migrate +If you want Active Record to not output anything, then running `rails db:migrate VERBOSE=false` will suppress all output. Changing Existing Migrations @@ -882,9 +885,9 @@ Changing Existing Migrations Occasionally you will make a mistake when writing a migration. If you have already run the migration then you cannot just edit the migration and run the migration again: Rails thinks it has already run the migration and so will do -nothing when you run `rake db:migrate`. You must rollback the migration (for -example with `rake db:rollback`), edit your migration and then run -`rake db:migrate` to run the corrected version. +nothing when you run `rails db:migrate`. You must rollback the migration (for +example with `bin/rails db:rollback`), edit your migration and then run +`rails db:migrate` to run the corrected version. In general, editing existing migrations is not a good idea. You will be creating extra work for yourself and your co-workers and cause major headaches @@ -966,7 +969,7 @@ this, then you should set the schema format to `:sql`. Instead of using Active Record's schema dumper, the database's structure will be dumped using a tool specific to the database (via the `db:structure:dump` -Rake task) into `db/structure.sql`. For example, for PostgreSQL, the `pg_dump` +rails task) into `db/structure.sql`. For example, for PostgreSQL, the `pg_dump` utility is used. For MySQL, this file will contain the output of `SHOW CREATE TABLE` for the various tables. @@ -1006,10 +1009,13 @@ such features, the `execute` method can be used to execute arbitrary SQL. Migrations and Seed Data ------------------------ -Some people use migrations to add data to the database: +The main purpose of Rails' migration feature is to issue commands that modify the +schema using a consistent process. Migrations can also be used +to add or modify data. This is useful in an existing database that can't be destroyed +and recreated, such as a production database. ```ruby -class AddInitialProducts < ActiveRecord::Migration +class AddInitialProducts < ActiveRecord::Migration[5.0] def up 5.times do |i| Product.create(name: "Product ##{i}", description: "A product.") @@ -1022,9 +1028,11 @@ class AddInitialProducts < ActiveRecord::Migration end ``` -However, Rails has a 'seeds' feature that should be used for seeding a database -with initial data. It's a really simple feature: just fill up `db/seeds.rb` -with some Ruby code, and run `rake db:seed`: +To add initial data after a database is created, Rails has a built-in +'seeds' feature that makes the process quick and easy. This is especially +useful when reloading the database frequently in development and test environments. +It's easy to get started with this feature: just fill up `db/seeds.rb` with some +Ruby code, and run `rails db:seed`: ```ruby 5.times do |i| diff --git a/guides/source/active_record_postgresql.md b/guides/source/active_record_postgresql.md index fe112a4708..68c6a77882 100644 --- a/guides/source/active_record_postgresql.md +++ b/guides/source/active_record_postgresql.md @@ -39,7 +39,7 @@ create_table :documents do |t| end # app/models/document.rb -class Document < ActiveRecord::Base +class Document < ApplicationRecord end # Usage @@ -63,7 +63,7 @@ add_index :books, :tags, using: 'gin' add_index :books, :ratings, using: 'gin' # app/models/book.rb -class Book < ActiveRecord::Base +class Book < ApplicationRecord end # Usage @@ -84,8 +84,9 @@ Book.where("array_length(ratings, 1) >= 3") ### Hstore * [type definition](http://www.postgresql.org/docs/current/static/hstore.html) +* [functions and operators](http://www.postgresql.org/docs/current/static/hstore.html#AEN167712) -NOTE: you need to enable the `hstore` extension to use hstore. +NOTE: You need to enable the `hstore` extension to use hstore. ```ruby # db/migrate/20131009135255_create_profiles.rb @@ -97,7 +98,7 @@ ActiveRecord::Schema.define do end # app/models/profile.rb -class Profile < ActiveRecord::Base +class Profile < ApplicationRecord end # Usage @@ -108,6 +109,9 @@ profile.settings # => {"color"=>"blue", "resolution"=>"800x600"} profile.settings = {"color" => "yellow", "resolution" => "1280x1024"} profile.save! + +Profile.where("settings->'color' = ?", "yellow") +#=> #<ActiveRecord::Relation [#<Profile id: 1, settings: {"color"=>"yellow", "resolution"=>"1280x1024"}>]> ``` ### JSON @@ -122,7 +126,7 @@ create_table :events do |t| end # app/models/event.rb -class Event < ActiveRecord::Base +class Event < ApplicationRecord end # Usage @@ -150,7 +154,7 @@ create_table :events do |t| end # app/models/event.rb -class Event < ActiveRecord::Base +class Event < ApplicationRecord end # Usage @@ -200,7 +204,7 @@ create_table :contacts do |t| end # app/models/contact.rb -class Contact < ActiveRecord::Base +class Contact < ApplicationRecord end # Usage @@ -220,15 +224,26 @@ normal text columns: ```ruby # db/migrate/20131220144913_create_articles.rb -execute <<-SQL - CREATE TYPE article_status AS ENUM ('draft', 'published'); -SQL -create_table :articles do |t| - t.column :status, :article_status +def up + execute <<-SQL + CREATE TYPE article_status AS ENUM ('draft', 'published'); + SQL + create_table :articles do |t| + t.column :status, :article_status + end +end + +# NOTE: It's important to drop table before dropping enum. +def down + drop_table :articles + + execute <<-SQL + DROP TYPE article_status; + SQL end # app/models/article.rb -class Article < ActiveRecord::Base +class Article < ApplicationRecord end # Usage @@ -240,23 +255,50 @@ article.status = "published" article.save! ``` +To add a new value before/after existing one you should use [ALTER TYPE](http://www.postgresql.org/docs/current/static/sql-altertype.html): + +```ruby +# db/migrate/20150720144913_add_new_state_to_articles.rb +# NOTE: ALTER TYPE ... ADD VALUE cannot be executed inside of a transaction block so here we are using disable_ddl_transaction! +disable_ddl_transaction! + +def up + execute <<-SQL + ALTER TYPE article_status ADD VALUE IF NOT EXISTS 'archived' AFTER 'published'; + SQL +end +``` + +NOTE: ENUM values can't be dropped currently. You can read why [here](http://www.postgresql.org/message-id/29F36C7C98AB09499B1A209D48EAA615B7653DBC8A@mail2a.alliedtesting.com). + +Hint: to show all the values of the all enums you have, you should call this query in `bin/rails db` or `psql` console: + +```sql +SELECT n.nspname AS enum_schema, + t.typname AS enum_name, + e.enumlabel AS enum_value + FROM pg_type t + JOIN pg_enum e ON t.oid = e.enumtypid + JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace +``` + ### UUID * [type definition](http://www.postgresql.org/docs/current/static/datatype-uuid.html) * [pgcrypto generator function](http://www.postgresql.org/docs/current/static/pgcrypto.html#AEN159361) * [uuid-ossp generator functions](http://www.postgresql.org/docs/current/static/uuid-ossp.html) -NOTE: you need to enable the `pgcrypto` (only PostgreSQL >= 9.4) or `uuid-ossp` +NOTE: You need to enable the `pgcrypto` (only PostgreSQL >= 9.4) or `uuid-ossp` extension to use uuid. ```ruby # db/migrate/20131220144913_create_revisions.rb create_table :revisions do |t| - t.column :identifier, :uuid + t.uuid :identifier end # app/models/revision.rb -class Revision < ActiveRecord::Base +class Revision < ApplicationRecord end # Usage @@ -279,12 +321,12 @@ create_table :comments, id: :uuid, default: 'gen_random_uuid()' do |t| end # app/models/post.rb -class Post < ActiveRecord::Base +class Post < ApplicationRecord has_many :comments end # app/models/comment.rb -class Comment < ActiveRecord::Base +class Comment < ApplicationRecord belongs_to :post end ``` @@ -303,7 +345,7 @@ create_table :users, force: true do |t| end # app/models/device.rb -class User < ActiveRecord::Base +class User < ApplicationRecord end # Usage @@ -332,7 +374,7 @@ create_table(:devices, force: true) do |t| end # app/models/device.rb -class Device < ActiveRecord::Base +class Device < ApplicationRecord end # Usage @@ -361,7 +403,7 @@ A point is casted to an array containing `x` and `y` coordinates. UUID Primary Keys ----------------- -NOTE: you need to enable the `pgcrypto` (only PostgreSQL >= 9.4) or `uuid-ossp` +NOTE: You need to enable the `pgcrypto` (only PostgreSQL >= 9.4) or `uuid-ossp` extension to generate random UUIDs. ```ruby @@ -372,7 +414,7 @@ create_table :devices, id: :uuid, default: 'gen_random_uuid()' do |t| end # app/models/device.rb -class Device < ActiveRecord::Base +class Device < ApplicationRecord end # Usage @@ -396,7 +438,7 @@ end execute "CREATE INDEX documents_idx ON documents USING gin(to_tsvector('english', title || ' ' || body));" # app/models/document.rb -class Document < ActiveRecord::Base +class Document < ApplicationRecord end # Usage @@ -446,7 +488,7 @@ CREATE VIEW articles AS SQL # app/models/article.rb -class Article < ActiveRecord::Base +class Article < ApplicationRecord self.primary_key = "id" def archive! update_attribute :archived, true diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index 4b4d70d3ce..63658e7c8b 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -10,7 +10,7 @@ After reading this guide, you will know: * How to find records using a variety of methods and conditions. * How to specify the order, retrieved attributes, grouping, and other properties of the found records. * How to use eager loading to reduce the number of database queries needed for data retrieval. -* How to use dynamic finders methods. +* How to use dynamic finder methods. * How to use method chaining to use multiple ActiveRecord methods together. * How to check for the existence of particular records. * How to perform various calculations on Active Record models. @@ -25,7 +25,7 @@ Code examples throughout this guide will refer to one or more of the following m TIP: All of the following models use `id` as the primary key, unless specified otherwise. ```ruby -class Client < ActiveRecord::Base +class Client < ApplicationRecord has_one :address has_many :orders has_and_belongs_to_many :roles @@ -33,19 +33,19 @@ end ``` ```ruby -class Address < ActiveRecord::Base +class Address < ApplicationRecord belongs_to :client end ``` ```ruby -class Order < ActiveRecord::Base +class Order < ApplicationRecord belongs_to :client, counter_cache: true end ``` ```ruby -class Role < ActiveRecord::Base +class Role < ApplicationRecord has_and_belongs_to_many :clients end ``` @@ -59,7 +59,7 @@ To retrieve objects from the database, Active Record provides several finder met The methods are: -* `bind` +* `find` * `create_with` * `distinct` * `eager_load` @@ -69,6 +69,7 @@ The methods are: * `having` * `includes` * `joins` +* `left_outer_joins` * `limit` * `lock` * `none` @@ -80,7 +81,7 @@ The methods are: * `reorder` * `reverse_order` * `select` -* `uniq` +* `distinct` * `where` All of the above methods return an instance of `ActiveRecord::Relation`. @@ -169,7 +170,7 @@ TIP: The retrieved record may vary depending on the database engine. #### `first` -The `first` method finds the first record ordered by the primary key. For example: +The `first` method finds the first record ordered by primary key (default). For example: ```ruby client = Client.first @@ -184,6 +185,8 @@ SELECT * FROM clients ORDER BY clients.id ASC LIMIT 1 The `first` method returns `nil` if no matching record is found and no exception will be raised. +If your [default scope](active_record_querying.html#applying-a-default-scope) contains an order method, `first` will return the first record according to this ordering. + You can pass in a numerical argument to the `first` method to return up to that number of results. For example ```ruby @@ -201,11 +204,24 @@ The SQL equivalent of the above is: SELECT * FROM clients ORDER BY clients.id ASC LIMIT 3 ``` +On a collection that is ordered using `order`, `first` will return the first record ordered by the specified attribute for `order`. + +```ruby +client = Client.order(:first_name).first +# => #<Client id: 2, first_name: "Fifo"> +``` + +The SQL equivalent of the above is: + +```sql +SELECT * FROM clients ORDER BY clients.first_name ASC LIMIT 1 +``` + The `first!` method behaves exactly like `first`, except that it will raise `ActiveRecord::RecordNotFound` if no matching record is found. #### `last` -The `last` method finds the last record ordered by the primary key. For example: +The `last` method finds the last record ordered by primary key (default). For example: ```ruby client = Client.last @@ -220,6 +236,8 @@ SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1 The `last` method returns `nil` if no matching record is found and no exception will be raised. +If your [default scope](active_record_querying.html#applying-a-default-scope) contains an order method, `last` will return the last record according to this ordering. + You can pass in a numerical argument to the `last` method to return up to that number of results. For example ```ruby @@ -237,6 +255,19 @@ The SQL equivalent of the above is: SELECT * FROM clients ORDER BY clients.id DESC LIMIT 3 ``` +On a collection that is ordered using `order`, `last` will return the last record ordered by the specified attribute for `order`. + +```ruby +client = Client.order(:first_name).last +# => #<Client id: 220, first_name: "Sara"> +``` + +The SQL equivalent of the above is: + +```sql +SELECT * FROM clients ORDER BY clients.first_name DESC LIMIT 1 +``` + The `last!` method behaves exactly like `last`, except that it will raise `ActiveRecord::RecordNotFound` if no matching record is found. #### `find_by` @@ -317,7 +348,7 @@ end The `find_each` method accepts most of the options allowed by the regular `find` method, except for `:order` and `:limit`, which are reserved for internal use by `find_each`. -Three additional options, `:batch_size`, `:begin_at` and `:end_at`, are available as well. +Three additional options, `:batch_size`, `:start` and `:finish`, are available as well. **`:batch_size`** @@ -329,33 +360,35 @@ User.find_each(batch_size: 5000) do |user| end ``` -**`:begin_at`** +**`:start`** -By default, records are fetched in ascending order of the primary key, which must be an integer. The `:begin_at` option allows you to configure the first ID of the sequence whenever the lowest ID is not the one you need. This would be useful, for example, if you wanted to resume an interrupted batch process, provided you saved the last processed ID as a checkpoint. +By default, records are fetched in ascending order of the primary key, which must be an integer. The `:start` option allows you to configure the first ID of the sequence whenever the lowest ID is not the one you need. This would be useful, for example, if you wanted to resume an interrupted batch process, provided you saved the last processed ID as a checkpoint. For example, to send newsletters only to users with the primary key starting from 2000, and to retrieve them in batches of 5000: ```ruby -User.find_each(begin_at: 2000, batch_size: 5000) do |user| +User.find_each(start: 2000, batch_size: 5000) do |user| NewsMailer.weekly(user).deliver_now end ``` -Another example would be if you wanted multiple workers handling the same processing queue. You could have each worker handle 10000 records by setting the appropriate `:begin_at` option on each worker. +**`:finish`** -**`:end_at`** - -Similar to the `:begin_at` option, `:end_at` allows you to configure the last ID of the sequence whenever the highest ID is not the one you need. -This would be useful, for example, if you wanted to run a batch process, using a subset of records based on `:begin_at` and `:end_at` +Similar to the `:start` option, `:finish` allows you to configure the last ID of the sequence whenever the highest ID is not the one you need. +This would be useful, for example, if you wanted to run a batch process, using a subset of records based on `:start` and `:finish` For example, to send newsletters only to users with the primary key starting from 2000 up to 10000 and to retrieve them in batches of 5000: ```ruby -User.find_each(begin_at: 2000, end_at: 10000, batch_size: 5000) do |user| +User.find_each(start: 2000, finish: 10000, batch_size: 5000) do |user| NewsMailer.weekly(user).deliver_now end ``` +Another example would be if you wanted multiple workers handling the same +processing queue. You could have each worker handle 10000 records by setting the +appropriate `:start` and `:finish` options on each worker. + #### `find_in_batches` The `find_in_batches` method is similar to `find_each`, since both retrieve batches of records. The difference is that `find_in_batches` yields _batches_ to the block as an array of models, instead of individually. The following example will yield to the supplied block an array of up to 1000 invoices at a time, with the final block containing any remaining invoices: @@ -369,7 +402,7 @@ end ##### Options for `find_in_batches` -The `find_in_batches` method accepts the same `:batch_size`, `:begin_at` and `:end_at` options as `find_each`. +The `find_in_batches` method accepts the same `:batch_size`, `:start` and `:finish` options as `find_each`. Conditions ---------- @@ -390,7 +423,7 @@ Now what if that number could vary, say as an argument from somewhere? The find Client.where("orders_count = ?", params[:orders]) ``` -Active Record will go through the first element in the conditions value and any additional elements will replace the question marks `(?)` in the first element. +Active Record will take the first argument as the conditions string and any additional arguments will replace the question marks `(?)` in it. If you want to specify multiple conditions: @@ -418,7 +451,7 @@ TIP: For more information on the dangers of SQL injection, see the [Ruby on Rail #### Placeholder Conditions -Similar to the `(?)` replacement style of params, you can also specify keys/values hash in your array conditions: +Similar to the `(?)` replacement style of params, you can also specify keys in your conditions string along with a corresponding keys/values hash: ```ruby Client.where("created_at >= :start_date AND created_at <= :end_date", @@ -429,7 +462,7 @@ This makes for clearer readability if you have a large number of variable condit ### Hash Conditions -Active Record also allows you to pass in hash conditions which can increase the readability of your conditions syntax. With hash conditions, you pass in a hash with keys of the fields you want conditionalised and the values of how you want to conditionalise them: +Active Record also allows you to pass in hash conditions which can increase the readability of your conditions syntax. With hash conditions, you pass in a hash with keys of the fields you want qualified and the values of how you want to qualify them: NOTE: Only equality, range and subset checking are possible with Hash conditions. @@ -529,7 +562,7 @@ Client.order("orders_count ASC, created_at DESC") Client.order("orders_count ASC", "created_at DESC") ``` -If you want to call `order` multiple times e.g. in different context, new order will append previous one: +If you want to call `order` multiple times, subsequent orders will be appended to the first: ```ruby Client.order("orders_count ASC").order("created_at DESC") @@ -617,9 +650,9 @@ SELECT * FROM clients LIMIT 5 OFFSET 30 Group ----- -To apply a `GROUP BY` clause to the SQL fired by the finder, you can specify the `group` method on the find. +To apply a `GROUP BY` clause to the SQL fired by the finder, you can use the `group` method. -For example, if you want to find a collection of the dates orders were created on: +For example, if you want to find a collection of the dates on which orders were created: ```ruby Order.select("date(created_at) as ordered_date, sum(price) as total_price").group("date(created_at)") @@ -637,7 +670,7 @@ GROUP BY date(created_at) ### Total of grouped items -To get the total of grouped items on a single query call `count` after the `group`. +To get the total of grouped items on a single query, call `count` after the `group`. ```ruby Order.group(:status).count @@ -673,7 +706,7 @@ GROUP BY date(created_at) HAVING sum(price) > 100 ``` -This will return single order objects for each day, but only those that are ordered more than $100 in a day. +This returns the date and total price for each order object, grouped by the day they were ordered and where the price is more than $100. Overriding Conditions --------------------- @@ -703,8 +736,7 @@ Article.where(id: 10, trashed: false).unscope(where: :id) # SELECT "articles".* FROM "articles" WHERE trashed = 0 ``` -A relation which has used `unscope` will affect any relation it is -merged in to: +A relation which has used `unscope` will affect any relation into which it is merged: ```ruby Article.order('id asc').merge(Article.unscope(:order)) @@ -734,7 +766,7 @@ SELECT "articles".* FROM "articles" WHERE (id > 10) ORDER BY id desc LIMIT 20 The `reorder` method overrides the default scope order. For example: ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord has_many :comments, -> { order('posted_at DESC') } end @@ -748,7 +780,7 @@ SELECT * FROM articles WHERE id = 10 SELECT * FROM comments WHERE article_id = 10 ORDER BY name ``` -In case the `reorder` clause is not used, the SQL executed would be: +In the case where the `reorder` clause is not used, the SQL executed would be: ```sql SELECT * FROM articles WHERE id = 10 @@ -837,7 +869,7 @@ end Readonly Objects ---------------- -Active Record provides `readonly` method on a relation to explicitly disallow modification of any of the returned objects. Any attempt to alter a readonly record will not succeed, raising an `ActiveRecord::ReadOnlyRecord` exception. +Active Record provides the `readonly` method on a relation to explicitly disallow modification of any of the returned objects. Any attempt to alter a readonly record will not succeed, raising an `ActiveRecord::ReadOnlyRecord` exception. ```ruby client = Client.readonly.first @@ -883,7 +915,7 @@ This behavior can be turned off by setting `ActiveRecord::Base.lock_optimistical To override the name of the `lock_version` column, `ActiveRecord::Base` provides a class attribute called `locking_column`: ```ruby -class Client < ActiveRecord::Base +class Client < ApplicationRecord self.locking_column = :lock_client_column end ``` @@ -934,58 +966,63 @@ end Joining Tables -------------- -Active Record provides a finder method called `joins` for specifying `JOIN` clauses on the resulting SQL. There are multiple ways to use the `joins` method. +Active Record provides two finder methods for specifying `JOIN` clauses on the +resulting SQL: `joins` and `left_outer_joins`. +While `joins` should be used for `INNER JOIN` or custom queries, +`left_outer_joins` is used for queries using `LEFT OUTER JOIN`. + +### `joins` + +There are multiple ways to use the `joins` method. -### Using a String SQL Fragment +#### Using a String SQL Fragment You can just supply the raw SQL specifying the `JOIN` clause to `joins`: ```ruby -Client.joins('LEFT OUTER JOIN addresses ON addresses.client_id = clients.id') +Author.joins("INNER JOIN posts ON posts.author_id = author.id AND posts.published = 't'") ``` This will result in the following SQL: ```sql -SELECT clients.* FROM clients LEFT OUTER JOIN addresses ON addresses.client_id = clients.id +SELECT clients.* FROM clients INNER JOIN posts ON posts.author_id = author.id AND posts.published = 't' ``` -### Using Array/Hash of Named Associations - -WARNING: This method only works with `INNER JOIN`. +#### Using Array/Hash of Named Associations Active Record lets you use the names of the [associations](association_basics.html) defined on the model as a shortcut for specifying `JOIN` clauses for those associations when using the `joins` method. For example, consider the following `Category`, `Article`, `Comment`, `Guest` and `Tag` models: ```ruby -class Category < ActiveRecord::Base +class Category < ApplicationRecord has_many :articles end -class Article < ActiveRecord::Base +class Article < ApplicationRecord belongs_to :category has_many :comments has_many :tags end -class Comment < ActiveRecord::Base +class Comment < ApplicationRecord belongs_to :article has_one :guest end -class Guest < ActiveRecord::Base +class Guest < ApplicationRecord belongs_to :comment end -class Tag < ActiveRecord::Base +class Tag < ApplicationRecord belongs_to :article end ``` Now all of the following will produce the expected join queries using `INNER JOIN`: -#### Joining a Single Association +##### Joining a Single Association ```ruby Category.joins(:articles) @@ -998,7 +1035,7 @@ SELECT categories.* FROM categories INNER JOIN articles ON articles.category_id = categories.id ``` -Or, in English: "return a Category object for all categories with articles". Note that you will see duplicate categories if more than one article has the same category. If you want unique categories, you can use `Category.joins(:articles).uniq`. +Or, in English: "return a Category object for all categories with articles". Note that you will see duplicate categories if more than one article has the same category. If you want unique categories, you can use `Category.joins(:articles).distinct`. #### Joining Multiple Associations @@ -1016,7 +1053,7 @@ SELECT articles.* FROM articles Or, in English: "return all articles that have a category and at least one comment". Note again that articles with multiple comments will show up multiple times. -#### Joining Nested Associations (Single Level) +##### Joining Nested Associations (Single Level) ```ruby Article.joins(comments: :guest) @@ -1032,7 +1069,7 @@ SELECT articles.* FROM articles Or, in English: "return all articles that have a comment made by a guest." -#### Joining Nested Associations (Multiple Level) +##### Joining Nested Associations (Multiple Level) ```ruby Category.joins(articles: [{ comments: :guest }, :tags]) @@ -1048,9 +1085,11 @@ SELECT categories.* FROM categories INNER JOIN tags ON tags.article_id = articles.id ``` -### Specifying Conditions on the Joined Tables +Or, in English: "return all categories that have articles, where those articles have a comment made by a guest, and where those articles also have a tag." + +#### Specifying Conditions on the Joined Tables -You can specify conditions on the joined tables using the regular [Array](#array-conditions) and [String](#pure-string-conditions) conditions. [Hash conditions](#hash-conditions) provides a special syntax for specifying conditions for the joined tables: +You can specify conditions on the joined tables using the regular [Array](#array-conditions) and [String](#pure-string-conditions) conditions. [Hash conditions](#hash-conditions) provide a special syntax for specifying conditions for the joined tables: ```ruby time_range = (Time.now.midnight - 1.day)..Time.now.midnight @@ -1066,6 +1105,26 @@ Client.joins(:orders).where(orders: { created_at: time_range }) This will find all clients who have orders that were created yesterday, again using a `BETWEEN` SQL expression. +### `left_outer_joins` + +If you want to select a set of records whether or not they have associated +records you can use the `left_outer_joins` method. + +```ruby +Author.left_outer_joins(:posts).uniq.select('authors.*, COUNT(posts.*) AS posts_count').group('authors.id') +``` + +Which produces: + +```sql +SELECT DISTINCT authors.*, COUNT(posts.*) AS posts_count FROM "authors" +LEFT OUTER JOIN posts ON posts.author_id = authors.id GROUP BY authors.id +``` + +Which means: "return all authors with their count of posts, whether or not they +have any posts at all" + + Eager Loading Associations -------------------------- @@ -1089,7 +1148,7 @@ This code looks fine at the first sight. But the problem lies within the total n Active Record lets you specify in advance all the associations that are going to be loaded. This is possible by specifying the `includes` method of the `Model.find` call. With `includes`, Active Record ensures that all of the specified associations are loaded using the minimum possible number of queries. -Revisiting the above case, we could rewrite `Client.limit(10)` to use eager load addresses: +Revisiting the above case, we could rewrite `Client.limit(10)` to eager load addresses: ```ruby clients = Client.includes(:address).limit(10) @@ -1168,7 +1227,7 @@ Scoping allows you to specify commonly-used queries which can be referenced as m To define a simple scope, we use the `scope` method inside the class, passing the query that we'd like to run when this scope is called: ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord scope :published, -> { where(published: true) } end ``` @@ -1176,7 +1235,7 @@ end This is exactly the same as defining a class method, and which you use is a matter of personal preference: ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord def self.published where(published: true) end @@ -1186,7 +1245,7 @@ end Scopes are also chainable within scopes: ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord scope :published, -> { where(published: true) } scope :published_and_commented, -> { published.where("comments_count > 0") } end @@ -1210,7 +1269,7 @@ category.articles.published # => [published articles belonging to this category] Your scope can take arguments: ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord scope :created_before, ->(time) { where("created_at < ?", time) } end ``` @@ -1224,7 +1283,7 @@ Article.created_before(Time.zone.now) However, this is just duplicating the functionality that would be provided to you by a class method. ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord def self.created_before(time) where("created_at < ?", time) end @@ -1243,7 +1302,7 @@ If we wish for a scope to be applied across all queries to the model we can use `default_scope` method within the model itself. ```ruby -class Client < ActiveRecord::Base +class Client < ApplicationRecord default_scope { where("removed_at IS NULL") } end ``` @@ -1259,7 +1318,7 @@ If you need to do more complex things with a default scope, you can alternativel define it as a class method: ```ruby -class Client < ActiveRecord::Base +class Client < ApplicationRecord def self.default_scope # Should return an ActiveRecord::Relation. end @@ -1270,7 +1329,7 @@ NOTE: The `default_scope` is also applied while creating/building a record. It is not applied while updating a record. E.g.: ```ruby -class Client < ActiveRecord::Base +class Client < ApplicationRecord default_scope { where(active: true) } end @@ -1283,7 +1342,7 @@ Client.unscoped.new # => #<Client id: nil, active: nil> Just like `where` clauses scopes are merged using `AND` conditions. ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord scope :active, -> { where state: 'active' } scope :inactive, -> { where state: 'inactive' } end @@ -1312,7 +1371,7 @@ One important caveat is that `default_scope` will be prepended in `scope` and `where` conditions. ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord default_scope { where state: 'pending' } scope :active, -> { where state: 'active' } scope :inactive, -> { where state: 'inactive' } @@ -1343,8 +1402,15 @@ Client.unscoped.load This method removes all scoping and will do a normal query on the table. -Note that chaining `unscoped` with a `scope` does not work. In these cases, it is -recommended that you use the block form of `unscoped`: +```ruby +Client.unscoped.all +# SELECT "clients".* FROM "clients" + +Client.where(published: false).unscoped.all +# SELECT "clients".* FROM "clients" +``` + +`unscoped` can also accept a block. ```ruby Client.unscoped { @@ -1361,6 +1427,36 @@ You can specify an exclamation point (`!`) on the end of the dynamic finders to If you want to find both by name and locked, you can chain these finders together by simply typing "`and`" between the fields. For example, `Client.find_by_first_name_and_locked("Ryan", true)`. +Enums +----- + +The `enum` macro maps an integer column to a set of possible values. + +```ruby +class Book < ApplicationRecord + enum availability: [:available, :unavailable] +end +``` + +This will automatically create the corresponding [scopes](#scopes) to query the +model. Methods to transition between states and query the current state are also +added. + +```ruby +# Both examples below query just available books. +Book.available +# or +Book.where(availability: :available) + +book = Book.new(availability: :available) +book.available? # => true +book.unavailable! # => true +book.available? # => false +``` + +Read the full documentation about enums +[in the Rails API docs](http://api.rubyonrails.org/classes/ActiveRecord/Enum.html). + Understanding The Method Chaining --------------------------------- @@ -1426,7 +1522,7 @@ It's common that you need to find a record or create it if it doesn't exist. You ### `find_or_create_by` -The `find_or_create_by` method checks whether a record with the attributes exists. If it doesn't, then `create` is called. Let's see an example. +The `find_or_create_by` method checks whether a record with the specified attributes exists. If it doesn't, then `create` is called. Let's see an example. Suppose you want to find a client named 'Andy', and if there's none, create one. You can do so by running: @@ -1589,7 +1685,7 @@ a large or often-running query. However, any model method overrides will not be available. For example: ```ruby -class Client < ActiveRecord::Base +class Client < ApplicationRecord def name "I am #{super}" end @@ -1624,7 +1720,7 @@ Person.ids ``` ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord self.primary_key = "person_id" end @@ -1866,6 +1962,6 @@ following pointers may be helpful: * SQLite3: [EXPLAIN QUERY PLAN](http://www.sqlite.org/eqp.html) -* MySQL: [EXPLAIN Output Format](http://dev.mysql.com/doc/refman/5.6/en/explain-output.html) +* MySQL: [EXPLAIN Output Format](http://dev.mysql.com/doc/refman/5.7/en/explain-output.html) * PostgreSQL: [Using EXPLAIN](http://www.postgresql.org/docs/current/static/using-explain.html) diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md index 71ca7a0f66..dd7adf09a2 100644 --- a/guides/source/active_record_validations.md +++ b/guides/source/active_record_validations.md @@ -20,7 +20,7 @@ Validations Overview Here's an example of a very simple validation: ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true end @@ -80,7 +80,7 @@ method to determine whether an object is already in the database or not. Consider the following simple Active Record class: ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord end ``` @@ -149,13 +149,15 @@ false` as an argument. This technique should be used with caution. ### `valid?` and `invalid?` -To verify whether or not an object is valid, Rails uses the `valid?` method. -You can also use this method on your own. `valid?` triggers your validations +Before saving an ActiveRecord object, Rails runs your validations. +If these validations produce any errors, Rails does not save the object. + +You can also run these validations on your own. `valid?` triggers your validations and returns true if no errors were found in the object, and false otherwise. As you saw above: ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true end @@ -168,11 +170,12 @@ through the `errors.messages` instance method, which returns a collection of err By definition, an object is valid if this collection is empty after running validations. -Note that an object instantiated with `new` will not report errors even if it's -technically invalid, because validations are not run when using `new`. +Note that an object instantiated with `new` will not report errors +even if it's technically invalid, because validations are automatically run +only when the object is saved, such as with the `create` or `save` methods. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true end @@ -218,7 +221,7 @@ it doesn't verify the validity of the object as a whole. It only checks to see whether there are errors found on an individual attribute of the object. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true end @@ -236,13 +239,13 @@ To check which validations failed on an invalid attribute, you can use key to get the symbol of the validator: ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true end >> person = Person.new >> person.valid? ->> person.errors.details[:name] #=> [{error: :blank}] +>> person.errors.details[:name] # => [{error: :blank}] ``` Using `details` with custom validators is covered in the [Working with @@ -273,12 +276,16 @@ available helpers. This method validates that a checkbox on the user interface was checked when a form was submitted. This is typically used when the user needs to agree to your application's terms of service, confirm that some text is read, or any similar -concept. This validation is very specific to web applications and this -'acceptance' does not need to be recorded anywhere in your database (if you -don't have a field for it, the helper will just create a virtual attribute). +concept. + +This validation is very specific to web applications and this +'acceptance' does not need to be recorded anywhere in your database. If you +don't have a field for it, the helper will just create a virtual attribute. If +the field does exist in your database, the `accept` option must be set to +`true` or else the validation will not run. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :terms_of_service, acceptance: true end ``` @@ -290,7 +297,7 @@ It can receive an `:accept` option, which determines the value that will be considered acceptance. It defaults to "1" and can be easily changed. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :terms_of_service, acceptance: { accept: 'yes' } end ``` @@ -302,7 +309,7 @@ and they also need to be validated. When you try to save your object, `valid?` will be called upon each one of the associated objects. ```ruby -class Library < ActiveRecord::Base +class Library < ApplicationRecord has_many :books validates_associated :books end @@ -325,7 +332,7 @@ or a password. This validation creates a virtual attribute whose name is the name of the field that has to be confirmed with "_confirmation" appended. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :email, confirmation: true end ``` @@ -342,12 +349,22 @@ confirmation, make sure to add a presence check for the confirmation attribute (we'll take a look at `presence` later on in this guide): ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :email, confirmation: true validates :email_confirmation, presence: true end ``` +There is also a `:case_sensitive` option that you can use to define whether the +confirmation constraint will be case sensitive or not. This option defaults to +true. + +```ruby +class Person < ApplicationRecord + validates :email, confirmation: { case_sensitive: false } +end +``` + The default error message for this helper is _"doesn't match confirmation"_. ### `exclusion` @@ -356,7 +373,7 @@ This helper validates that the attributes' values are not included in a given set. In fact, this set can be any enumerable object. ```ruby -class Account < ActiveRecord::Base +class Account < ApplicationRecord validates :subdomain, exclusion: { in: %w(www us ca jp), message: "%{value} is reserved." } end @@ -376,7 +393,7 @@ This helper validates the attributes' values by testing whether they match a given regular expression, which is specified using the `:with` option. ```ruby -class Product < ActiveRecord::Base +class Product < ApplicationRecord validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/, message: "only allows letters" } end @@ -392,7 +409,7 @@ This helper validates that the attributes' values are included in a given set. In fact, this set can be any enumerable object. ```ruby -class Coffee < ActiveRecord::Base +class Coffee < ApplicationRecord validates :size, inclusion: { in: %w(small medium large), message: "%{value} is not a valid size" } end @@ -411,7 +428,7 @@ This helper validates the length of the attributes' values. It provides a variety of options, so you can specify length constraints in different ways: ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, length: { minimum: 2 } validates :bio, length: { maximum: 500 } validates :password, length: { in: 6..20 } @@ -434,27 +451,12 @@ number corresponding to the length constraint being used. You can still use the `:message` option to specify an error message. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :bio, length: { maximum: 1000, too_long: "%{count} characters is the maximum allowed" } end ``` -This helper counts characters by default, but you can split the value in a -different way using the `:tokenizer` option: - -```ruby -class Essay < ActiveRecord::Base - validates :content, length: { - minimum: 300, - maximum: 400, - tokenizer: lambda { |str| str.split(/\s+/) }, - too_short: "must have at least %{count} words", - too_long: "must have at most %{count} words" - } -end -``` - Note that the default error messages are plural (e.g., "is too short (minimum is %{count} characters)"). For this reason, when `:minimum` is 1 you should provide a personalized message or use `presence: true` instead. When @@ -481,7 +483,7 @@ WARNING. Note that the regular expression above allows a trailing newline character. ```ruby -class Player < ActiveRecord::Base +class Player < ApplicationRecord validates :points, numericality: true validates :games_played, numericality: { only_integer: true } end @@ -519,7 +521,7 @@ This helper validates that the specified attributes are not empty. It uses the is, a string that is either empty or consists of whitespace. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, :login, :email, presence: true end ``` @@ -529,7 +531,7 @@ whether the associated object itself is present, and not the foreign key used to map the association. ```ruby -class LineItem < ActiveRecord::Base +class LineItem < ApplicationRecord belongs_to :order validates :order, presence: true end @@ -539,7 +541,7 @@ In order to validate associated records whose presence is required, you must specify the `:inverse_of` option for the association: ```ruby -class Order < ActiveRecord::Base +class Order < ApplicationRecord has_many :line_items, inverse_of: :order end ``` @@ -566,7 +568,7 @@ This helper validates that the specified attributes are absent. It uses the is, a string that is either empty or consists of whitespace. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, :login, :email, absence: true end ``` @@ -576,7 +578,7 @@ whether the associated object itself is absent, and not the foreign key used to map the association. ```ruby -class LineItem < ActiveRecord::Base +class LineItem < ApplicationRecord belongs_to :order validates :order, absence: true end @@ -586,7 +588,7 @@ In order to validate associated records whose absence is required, you must specify the `:inverse_of` option for the association: ```ruby -class Order < ActiveRecord::Base +class Order < ApplicationRecord has_many :line_items, inverse_of: :order end ``` @@ -609,7 +611,7 @@ with the same value for a column that you intend to be unique. To avoid that, you must create a unique index on that column in your database. ```ruby -class Account < ActiveRecord::Base +class Account < ApplicationRecord validates :email, uniqueness: true end ``` @@ -621,19 +623,19 @@ There is a `:scope` option that you can use to specify one or more attributes th are used to limit the uniqueness check: ```ruby -class Holiday < ActiveRecord::Base +class Holiday < ApplicationRecord validates :name, uniqueness: { scope: :year, message: "should happen once per year" } end ``` -Should you wish to create a database constraint to prevent possible violations of a uniqueness validation using the `:scope` option, you must create a unique index on both columns in your database. See [the MySQL manual](http://dev.mysql.com/doc/refman/5.6/en/multiple-column-indexes.html) for more details about multiple column indexes or [the PostgreSQL manual](http://www.postgresql.org/docs/current/static/ddl-constraints.html) for examples of unique constraints that refer to a group of columns. +Should you wish to create a database constraint to prevent possible violations of a uniqueness validation using the `:scope` option, you must create a unique index on both columns in your database. See [the MySQL manual](http://dev.mysql.com/doc/refman/5.7/en/multiple-column-indexes.html) for more details about multiple column indexes or [the PostgreSQL manual](http://www.postgresql.org/docs/current/static/ddl-constraints.html) for examples of unique constraints that refer to a group of columns. There is also a `:case_sensitive` option that you can use to define whether the uniqueness constraint will be case sensitive or not. This option defaults to true. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, uniqueness: { case_sensitive: false } end ``` @@ -656,7 +658,7 @@ class GoodnessValidator < ActiveModel::Validator end end -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates_with GoodnessValidator end ``` @@ -684,7 +686,7 @@ class GoodnessValidator < ActiveModel::Validator end end -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates_with GoodnessValidator, fields: [:first_name, :last_name] end ``` @@ -697,7 +699,7 @@ If your validator is complex enough that you want instance variables, you can easily use a plain old Ruby object instead: ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validate do |person| GoodnessValidator.new(person).validate end @@ -726,7 +728,7 @@ passed to `validates_each` will be tested against it. In the following example, we don't want names and surnames to begin with lower case. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates_each :name, :surname do |record, attr, value| record.errors.add(attr, 'must start with upper case') if value =~ /\A[[:lower:]]/ end @@ -749,7 +751,7 @@ The `:allow_nil` option skips the validation when the value being validated is `nil`. ```ruby -class Coffee < ActiveRecord::Base +class Coffee < ApplicationRecord validates :size, inclusion: { in: %w(small medium large), message: "%{value} is not a valid size" }, allow_nil: true end @@ -762,7 +764,7 @@ will let validation pass if the attribute's value is `blank?`, like `nil` or an empty string for example. ```ruby -class Topic < ActiveRecord::Base +class Topic < ApplicationRecord validates :title, length: { is: 5 }, allow_blank: true end @@ -775,7 +777,36 @@ Topic.create(title: nil).valid? # => true As you've already seen, the `:message` option lets you specify the message that will be added to the `errors` collection when validation fails. When this option is not used, Active Record will use the respective default error message -for each validation helper. +for each validation helper. The `:message` option accepts a `String` or `Proc`. + +A `String` `:message` value can optionally contain any/all of `%{value}`, +`%{attribute}`, and `%{model}` which will be dynamically replaced when +validation fails. + +A `Proc` `:message` value is given two arguments: a message key for i18n, and +a hash with `:model`, `:attribute`, and `:value` key-value pairs. + +```ruby +class Person < ApplicationRecord + # Hard-coded message + validates :name, presence: { message: "must be given please" } + + # Message with dynamic attribute value. %{value} will be replaced with + # the actual value of the attribute. %{attribute} and %{model} also + # available. + validates :age, numericality: { message: "%{value} seems wrong" } + + # Proc + validates :username, + uniqueness: { + # key = "activerecord.errors.models.person.attributes.username.taken" + # data = { model: "Person", attribute: "Username", value: <username> } + message: ->(key, data) do + "#{data[:value]} taken! Try again #{Time.zone.tomorrow}" + end + } +end +``` ### `:on` @@ -787,7 +818,7 @@ new record is created or `on: :update` to run the validation only when a record is updated. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord # it will be possible to update email with a duplicated value validates :email, uniqueness: true, on: :create @@ -806,7 +837,7 @@ You can also specify validations to be strict and raise `ActiveModel::StrictValidationFailed` when the object is invalid. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: { strict: true } end @@ -816,7 +847,7 @@ Person.new.valid? # => ActiveModel::StrictValidationFailed: Name can't be blank There is also the ability to pass a custom exception to the `:strict` option. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :token, presence: true, uniqueness: true, strict: TokenGenerationException end @@ -840,7 +871,7 @@ to the name of a method that will get called right before validation happens. This is the most commonly used option. ```ruby -class Order < ActiveRecord::Base +class Order < ApplicationRecord validates :card_number, presence: true, if: :paid_with_card? def paid_with_card? @@ -856,7 +887,7 @@ contain valid Ruby code. You should use this option only when the string represents a really short condition. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :surname, presence: true, if: "name.nil?" end ``` @@ -869,7 +900,7 @@ inline condition instead of a separate method. This option is best suited for one-liners. ```ruby -class Account < ActiveRecord::Base +class Account < ApplicationRecord validates :password, confirmation: true, unless: Proc.new { |a| a.password.blank? } end @@ -881,7 +912,7 @@ Sometimes it is useful to have multiple validations use one condition. It can be easily achieved using `with_options`. ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord with_options if: :is_admin? do |admin| admin.validates :password, length: { minimum: 10 } admin.validates :email, presence: true @@ -899,7 +930,7 @@ should happen, an `Array` can be used. Moreover, you can apply both `:if` and `:unless` to the same validation. ```ruby -class Computer < ActiveRecord::Base +class Computer < ApplicationRecord validates :mouse, presence: true, if: ["market.retail?", :desktop?], unless: Proc.new { |c| c.trackpad.present? } @@ -953,7 +984,7 @@ class EmailValidator < ActiveModel::EachValidator end end -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :email, presence: true, email: true end ``` @@ -972,8 +1003,12 @@ class method, passing in the symbols for the validation methods' names. You can pass more than one symbol for each class method and the respective validations will be run in the same order as they were registered. +The `valid?` method will verify that the errors collection is empty, +so your custom validation methods should add errors to it when you +wish validation to fail: + ```ruby -class Invoice < ActiveRecord::Base +class Invoice < ApplicationRecord validate :expiration_date_cannot_be_in_the_past, :discount_cannot_be_greater_than_total_value @@ -991,12 +1026,13 @@ class Invoice < ActiveRecord::Base end ``` -By default such validations will run every time you call `valid?`. It is also -possible to control when to run these custom validations by giving an `:on` -option to the `validate` method, with either: `:create` or `:update`. +By default, such validations will run every time you call `valid?` +or save the object. But it is also possible to control when to run these +custom validations by giving an `:on` option to the `validate` method, +with either: `:create` or `:update`. ```ruby -class Invoice < ActiveRecord::Base +class Invoice < ApplicationRecord validate :active_customer, on: :create def active_customer @@ -1017,7 +1053,7 @@ The following is a list of the most commonly used methods. Please refer to the ` Returns an instance of the class `ActiveModel::Errors` containing all errors. Each key is the attribute name and the value is an array of strings with all errors. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true, length: { minimum: 3 } end @@ -1036,7 +1072,7 @@ person.errors.messages # => {} `errors[]` is used when you want to check the error messages for a specific attribute. It returns an array of strings with all error messages for the given attribute, each string with one error message. If there are no errors related to the attribute, it returns an empty array. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true, length: { minimum: 3 } end @@ -1061,7 +1097,7 @@ The `add` method lets you add an error message related to a particular attribute The `errors.full_messages` method (or its equivalent, `errors.to_a`) returns the error messages in a user-friendly format, with the capitalized attribute name prepended to each message, as shown in the examples below. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord def a_method_used_for_validation_purposes errors.add(:name, "cannot contain the characters !@#%*()_-+=") end @@ -1079,7 +1115,7 @@ person.errors.full_messages An equivalent to `errors#add` is to use `<<` to append a message to the `errors.messages` array for an attribute: ```ruby - class Person < ActiveRecord::Base + class Person < ApplicationRecord def a_method_used_for_validation_purposes errors.messages[:name] << "cannot contain the characters !@#%*()_-+=" end @@ -1100,7 +1136,7 @@ You can specify a validator type to the returned error details hash using the `errors.add` method. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord def a_method_used_for_validation_purposes errors.add(:name, :invalid_characters) end @@ -1116,7 +1152,7 @@ To improve the error details to contain the unallowed characters set for instanc you can pass additional keys to `errors.add`. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord def a_method_used_for_validation_purposes errors.add(:name, :invalid_characters, not_allowed: "!@#%*()_-+=") end @@ -1136,7 +1172,7 @@ validator type. You can add error messages that are related to the object's state as a whole, instead of being related to a specific attribute. You can use this method when you want to say that the object is invalid, no matter the values of its attributes. Since `errors[:base]` is an array, you can simply add a string to it and it will be used as an error message. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord def a_method_used_for_validation_purposes errors[:base] << "This person is invalid because ..." end @@ -1148,7 +1184,7 @@ end The `clear` method is used when you intentionally want to clear all the messages in the `errors` collection. Of course, calling `errors.clear` upon an invalid object won't actually make it valid: the `errors` collection will now be empty, but the next time you call `valid?` or any method that tries to save this object to the database, the validations will run again. If any of the validations fail, the `errors` collection will be filled again. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true, length: { minimum: 3 } end @@ -1171,7 +1207,7 @@ p.errors[:name] The `size` method returns the total number of error messages for the object. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true, length: { minimum: 3 } end diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index 01bf928407..0aca6db9b6 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -172,7 +172,7 @@ NOTE: Defined in `active_support/core_ext/object/duplicable.rb`. ### `deep_dup` -The `deep_dup` method returns deep copy of a given object. Normally, when you `dup` an object that contains other objects, Ruby does not `dup` them, so it creates a shallow copy of the object. If you have an array with a string, for example, it will look like this: +The `deep_dup` method returns a deep copy of a given object. Normally, when you `dup` an object that contains other objects, Ruby does not `dup` them, so it creates a shallow copy of the object. If you have an array with a string, for example, it will look like this: ```ruby array = ['string'] @@ -248,6 +248,13 @@ end @person.try { |p| "#{p.first_name} #{p.last_name}" } ``` +Note that `try` will swallow no-method errors, returning nil instead. If you want to protect against typos, use `try!` instead: + +```ruby +@number.try(:nest) # => nil +@number.try!(:nest) # NoMethodError: undefined method `nest' for 1:Fixnum +``` + NOTE: Defined in `active_support/core_ext/object/try.rb`. ### `class_eval(*args, &block)` @@ -390,7 +397,7 @@ The method `with_options` provides a way to factor out common options in a serie Given a default options hash, `with_options` yields a proxy object to a block. Within the block, methods called on the proxy are forwarded to the receiver with their options merged. For example, you get rid of the duplication in: ```ruby -class Account < ActiveRecord::Base +class Account < ApplicationRecord has_many :customers, dependent: :destroy has_many :products, dependent: :destroy has_many :invoices, dependent: :destroy @@ -401,7 +408,7 @@ end this way: ```ruby -class Account < ActiveRecord::Base +class Account < ApplicationRecord with_options dependent: :destroy do |assoc| assoc.has_many :customers assoc.has_many :products @@ -453,7 +460,7 @@ NOTE: Defined in `active_support/core_ext/object/instance_variables.rb`. #### `instance_variable_names` -The method `instance_variable_names` returns an array. Each name includes the "@" sign. +The method `instance_variable_names` returns an array. Each name includes the "@" sign. ```ruby class C @@ -510,17 +517,17 @@ Extensions to `Module` Using plain Ruby you can wrap methods with other methods, that's called _alias chaining_. -For example, let's say you'd like params to be strings in functional tests, as they are in real requests, but still want the convenience of assigning integers and other kind of values. To accomplish that you could wrap `ActionController::TestCase#process` this way in `test/test_helper.rb`: +For example, let's say you'd like params to be strings in functional tests, as they are in real requests, but still want the convenience of assigning integers and other kind of values. To accomplish that you could wrap `ActionDispatch::IntegrationTest#process` this way in `test/test_helper.rb`: ```ruby -ActionController::TestCase.class_eval do +ActionDispatch::IntegrationTest.class_eval do # save a reference to the original process method alias_method :original_process, :process # now redefine process and delegate to original_process - def process(action, params=nil, session=nil, flash=nil, http_method='GET') + def process('GET', path, params: nil, headers: nil, env: nil, xhr: false) params = Hash[*params.map {|k, v| [k, v.to_s]}.flatten] - original_process(action, params, session, flash, http_method) + original_process('GET', path, params: params) end end ``` @@ -530,10 +537,10 @@ That's the method `get`, `post`, etc., delegate the work to. That technique has a risk, it could be the case that `:original_process` was taken. To try to avoid collisions people choose some label that characterizes what the chaining is about: ```ruby -ActionController::TestCase.class_eval do +ActionDispatch::IntegrationTest.class_eval do def process_with_stringified_params(...) params = Hash[*params.map {|k, v| [k, v.to_s]}.flatten] - process_without_stringified_params(action, params, session, flash, http_method) + process_without_stringified_params(method, path, params: params) end alias_method :process_without_stringified_params, :process alias_method :process, :process_with_stringified_params @@ -543,10 +550,10 @@ end The method `alias_method_chain` provides a shortcut for that pattern: ```ruby -ActionController::TestCase.class_eval do +ActionDispatch::IntegrationTest.class_eval do def process_with_stringified_params(...) params = Hash[*params.map {|k, v| [k, v.to_s]}.flatten] - process_without_stringified_params(action, params, session, flash, http_method) + process_without_stringified_params(method, path, params: params) end alias_method_chain :process, :stringified_params end @@ -561,7 +568,7 @@ NOTE: Defined in `active_support/core_ext/module/aliasing.rb`. Model attributes have a reader, a writer, and a predicate. You can alias a model attribute having the corresponding three methods defined for you in one shot. As in other aliasing methods, the new name is the first argument, and the old name is the second (one mnemonic is that they go in the same order as if you did an assignment): ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord # You can refer to the email column as "login". # This can be meaningful for authentication code. alias_attribute :login, :email @@ -869,7 +876,7 @@ The macro `delegate` offers an easy way to forward methods. Let's imagine that users in some application have login information in the `User` model but name and other data in a separate `Profile` model: ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord has_one :profile end ``` @@ -877,7 +884,7 @@ end With that configuration you get a user's name via their profile, `user.profile.name`, but it could be handy to still be able to access such attribute directly: ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord has_one :profile def name @@ -889,7 +896,7 @@ end That is what `delegate` does for you: ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord has_one :profile delegate :name, to: :profile @@ -1703,6 +1710,20 @@ The method `parameterize` normalizes its receiver in a way that can be used in p "Kurt Gödel".parameterize # => "kurt-godel" ``` +To preserve the case of the string, set the `preserve_case` argument to true. By default, `preserve_case` is set to false. + +```ruby +"John Smith".parameterize(preserve_case: true) # => "John-Smith" +"Kurt Gödel".parameterize(preserve_case: true) # => "Kurt-Godel" +``` + +To use a custom separator, override the `separator` argument. + +```ruby +"John Smith".parameterize(separator: "_") # => "john\_smith" +"Kurt Gödel".parameterize(separator: "_") # => "kurt\_godel" +``` + In fact, the result string is wrapped in an instance of `ActiveSupport::Multibyte::Chars`. NOTE: Defined in `active_support/core_ext/string/inflections.rb`. @@ -1865,15 +1886,15 @@ The methods `to_date`, `to_time`, and `to_datetime` are basically convenience wr ```ruby "2010-07-27".to_date # => Tue, 27 Jul 2010 -"2010-07-27 23:37:00".to_time # => Tue Jul 27 23:37:00 UTC 2010 +"2010-07-27 23:37:00".to_time # => 2010-07-27 23:37:00 +0200 "2010-07-27 23:37:00".to_datetime # => Tue, 27 Jul 2010 23:37:00 +0000 ``` `to_time` receives an optional argument `:utc` or `:local`, to indicate which time zone you want the time in: ```ruby -"2010-07-27 23:42:00".to_time(:utc) # => Tue Jul 27 23:42:00 UTC 2010 -"2010-07-27 23:42:00".to_time(:local) # => Tue Jul 27 23:42:00 +0200 2010 +"2010-07-27 23:42:00".to_time(:utc) # => 2010-07-27 23:42:00 UTC +"2010-07-27 23:42:00".to_time(:local) # => 2010-07-27 23:42:00 +0200 ``` Default is `:utc`. @@ -2003,12 +2024,14 @@ Produce a string representation of a number rounded to a precision: Produce a string representation of a number as a human-readable number of bytes: ```ruby -123.to_s(:human_size) # => 123 Bytes -1234.to_s(:human_size) # => 1.21 KB -12345.to_s(:human_size) # => 12.1 KB -1234567.to_s(:human_size) # => 1.18 MB -1234567890.to_s(:human_size) # => 1.15 GB -1234567890123.to_s(:human_size) # => 1.12 TB +123.to_s(:human_size) # => 123 Bytes +1234.to_s(:human_size) # => 1.21 KB +12345.to_s(:human_size) # => 12.1 KB +1234567.to_s(:human_size) # => 1.18 MB +1234567890.to_s(:human_size) # => 1.15 GB +1234567890123.to_s(:human_size) # => 1.12 TB +1234567890123456.to_s(:human_size) # => 1.1 PB +1234567890123456789.to_s(:human_size) # => 1.07 EB ``` Produce a string representation of a number in human-readable words: @@ -2073,30 +2096,22 @@ Extensions to `BigDecimal` -------------------------- ### `to_s` -The method `to_s` is aliased to `to_formatted_s`. This provides a convenient way to display a BigDecimal value in floating-point notation: +The method `to_s` provides a default specifier of "F". This means that a simple call to `to_s` will result in floating point representation instead of engineering notation: ```ruby BigDecimal.new(5.00, 6).to_s # => "5.0" ``` -### `to_formatted_s` - -Te method `to_formatted_s` provides a default specifier of "F". This means that a simple call to `to_formatted_s` or `to_s` will result in floating point representation instead of engineering notation: - -```ruby -BigDecimal.new(5.00, 6).to_formatted_s # => "5.0" -``` - and that symbol specifiers are also supported: ```ruby -BigDecimal.new(5.00, 6).to_formatted_s(:db) # => "5.0" +BigDecimal.new(5.00, 6).to_s(:db) # => "5.0" ``` Engineering notation is still supported: ```ruby -BigDecimal.new(5.00, 6).to_formatted_s("e") # => "0.5E1" +BigDecimal.new(5.00, 6).to_s("e") # => "0.5E1" ``` Extensions to `Enumerable` diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md index e49abc41f4..0fd0112c9f 100644 --- a/guides/source/active_support_instrumentation.md +++ b/guides/source/active_support_instrumentation.md @@ -232,6 +232,7 @@ Active Record | `:sql` | SQL statement | | `:name` | Name of the operation | | `:connection_id` | `self.object_id` | +| `:binds` | Bind parameters | INFO. The adapters will add their own data as well. @@ -244,14 +245,6 @@ INFO. The adapters will add their own data as well. } ``` -### identity.active_record - -| Key | Value | -| ---------------- | ----------------------------------------- | -| `:line` | Primary Key of object in the identity map | -| `:name` | Record's class | -| `:connection_id` | `self.object_id` | - ### instantiation.active_record | Key | Value | @@ -403,6 +396,38 @@ INFO. Cache stores may add their own keys } ``` +Active Job +-------- + +### enqueue_at.active_job + +| Key | Value | +| ------------ | -------------------------------------- | +| `:adapter` | QueueAdapter object processing the job | +| `:job` | Job object | + +### enqueue.active_job + +| Key | Value | +| ------------ | -------------------------------------- | +| `:adapter` | QueueAdapter object processing the job | +| `:job` | Job object | + +### perform_start.active_job + +| Key | Value | +| ------------ | -------------------------------------- | +| `:adapter` | QueueAdapter object processing the job | +| `:job` | Job object | + +### perform.active_job + +| Key | Value | +| ------------ | -------------------------------------- | +| `:adapter` | QueueAdapter object processing the job | +| `:job` | Job object | + + Railties -------- @@ -433,7 +458,7 @@ The block receives the following arguments: * The name of the event * Time when it started * Time when it finished -* An unique ID for this event +* A unique ID for this event * The payload (described in previous sections) ```ruby diff --git a/guides/source/api_app.md b/guides/source/api_app.md index 29ca872254..64b6bb64f2 100644 --- a/guides/source/api_app.md +++ b/guides/source/api_app.md @@ -8,7 +8,7 @@ In this guide you will learn: * What Rails provides for API-only applications * How to configure Rails to start without any browser features -* How to decide which middlewares you will want to include +* How to decide which middleware you will want to include * How to decide which modules to use in your controller -------------------------------------------------------------------------------- @@ -44,11 +44,11 @@ using Rails is: "isn't using Rails to spit out some JSON overkill? Shouldn't I just use something like Sinatra?". For very simple APIs, this may be true. However, even in very HTML-heavy -applications, most of an application's logic is actually outside of the view +applications, most of an application's logic lives outside of the view layer. The reason most people use Rails is that it provides a set of defaults that -allows us to get up and running quickly without having to make a lot of trivial +allows developers to get up and running quickly, without having to make a lot of trivial decisions. Let's take a look at some of the things that Rails provides out of the box that are @@ -86,7 +86,7 @@ Handled at the middleware layer: and return just the headers on the way out. This makes `HEAD` work reliably in all Rails APIs. -While you could obviously build these up in terms of existing Rack middlewares, +While you could obviously build these up in terms of existing Rack middleware, this list demonstrates that the default Rails middleware stack provides a lot of value, even if you're "just generating JSON". @@ -97,7 +97,7 @@ Handled at the Action Pack layer: means not having to spend time thinking about how to model your API in terms of HTTP. - URL Generation: The flip side of routing is URL generation. A good API based - on HTTP includes URLs (see [the GitHub gist API](http://developer.github.com/v3/gists/) + on HTTP includes URLs (see [the GitHub Gist API](http://developer.github.com/v3/gists/) for an example). - Header and Redirection Responses: `head :no_content` and `redirect_to user_url(current_user)` come in handy. Sure, you could manually @@ -126,7 +126,7 @@ when configuring Active Record. **The short version is**: you may not have thought about which parts of Rails are still applicable even if you remove the view layer, but the answer turns out -to be "most of it". +to be most of it. The Basic Configuration ----------------------- @@ -143,11 +143,11 @@ $ rails new my_api --api This will do three main things for you: -- Configure your application to start with a more limited set of middlewares +- Configure your application to start with a more limited set of middleware than normal. Specifically, it will not include any middleware primarily useful for browser applications (like cookies support) by default. - Make `ApplicationController` inherit from `ActionController::API` instead of - `ActionController::Base`. As with middlewares, this will leave out any Action + `ActionController::Base`. As with middleware, this will leave out any Action Controller modules that provide functionalities primarily used by browser applications. - Configure the generators to skip generating views, helpers and assets when @@ -163,6 +163,14 @@ class definition: config.api_only = true ``` +Optionally, in `config/environments/development.rb` add the following line +to render error responses using the API format (JSON by default) when it +is a local request: + +```ruby +config.debug_exception_response_format = :api +``` + Finally, inside `app/controllers/application_controller.rb`, instead of: ```ruby @@ -177,39 +185,38 @@ class ApplicationController < ActionController::API end ``` -Choosing Middlewares +Choosing Middleware -------------------- -An API application comes with the following middlewares by default: +An API application comes with the following middleware by default: - `Rack::Sendfile` - `ActionDispatch::Static` -- `Rack::Lock` +- `ActionDispatch::LoadInterlock` - `ActiveSupport::Cache::Strategy::LocalCache::Middleware` +- `Rack::Runtime` - `ActionDispatch::RequestId` - `Rails::Rack::Logger` -- `Rack::Runtime` - `ActionDispatch::ShowExceptions` - `ActionDispatch::DebugExceptions` - `ActionDispatch::RemoteIp` - `ActionDispatch::Reloader` - `ActionDispatch::Callbacks` -- `ActionDispatch::ParamsParser` - `Rack::Head` - `Rack::ConditionalGet` - `Rack::ETag` -See the [internal middlewares](rails_on_rack.html#internal-middleware-stack) +See the [internal middleware](rails_on_rack.html#internal-middleware-stack) section of the Rack guide for further information on them. -Other plugins, including Active Record, may add additional middlewares. In -general, these middlewares are agnostic to the type of application you are +Other plugins, including Active Record, may add additional middleware. In +general, these middleware are agnostic to the type of application you are building, and make sense in an API-only Rails application. -You can get a list of all middlewares in your application via: +You can get a list of all middleware in your application via: ```bash -$ rake middleware +$ rails middleware ``` ### Using the Cache Middleware @@ -222,7 +229,7 @@ For instance, using the `stale?` method: ```ruby def show - @post = Post.find(params[:id]) + @post = Post.find(params[:id]) if stale?(last_modified: @post.updated_at) render json: @post @@ -241,7 +248,7 @@ cross-client caching in the call to `stale?`: ```ruby def show - @post = Post.find(params[:id]) + @post = Post.find(params[:id]) if stale?(last_modified: @post.updated_at, public: true) render json: @post @@ -255,9 +262,6 @@ subsequent inbound requests for the same URL. Think of it as page caching using HTTP semantics. -NOTE: This middleware is always outside of the `Rack::Lock` mutex, even in -single-threaded applications. - ### Using Rack::Sendfile When you use the `send_file` method inside a Rails controller, it sets the @@ -289,12 +293,9 @@ config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" Make sure to configure your server to support these options following the instructions in the `Rack::Sendfile` documentation. -NOTE: The `Rack::Sendfile` middleware is always outside of the `Rack::Lock` -mutex, even in single-threaded applications. +### Using ActionDispatch::Request -### Using ActionDispatch::ParamsParser - -`ActionDispatch::ParamsParser` will take parameters from the client in the JSON +`ActionDispatch::Request#params` will take parameters from the client in the JSON format and make them available in your controller inside `params`. To use this, your client will need to make a request with JSON-encoded parameters @@ -313,16 +314,16 @@ jQuery.ajax({ }); ``` -`ActionDispatch::ParamsParser` will see the `Content-Type` and your parameters +`ActionDispatch::Request` will see the `Content-Type` and your parameters will be: ```ruby { :person => { :firstName => "Yehuda", :lastName => "Katz" } } ``` -### Other Middlewares +### Other Middleware -Rails ships with a number of other middlewares that you might want to use in an +Rails ships with a number of other middleware that you might want to use in an API application, especially if one of your API clients is the browser: - `Rack::MethodOverride` @@ -333,13 +334,13 @@ API application, especially if one of your API clients is the browser: * `ActionDispatch::Session::CookieStore` * `ActionDispatch::Session::MemCacheStore` -Any of these middlewares can be added via: +Any of these middleware can be added via: ```ruby config.middleware.use Rack::MethodOverride ``` -### Removing Middlewares +### Removing Middleware If you don't want to use a middleware that is included by default in the API-only middleware set, you can remove it with: @@ -348,7 +349,7 @@ middleware set, you can remove it with: config.middleware.delete ::Rack::Sendfile ``` -Keep in mind that removing these middlewares will remove support for certain +Keep in mind that removing these middleware will remove support for certain features in Action Controller. Choosing Controller Modules @@ -357,25 +358,24 @@ Choosing Controller Modules An API application (using `ActionController::API`) comes with the following controller modules by default: -- `ActionController::UrlFor`: Makes `url_for` and friends available. +- `ActionController::UrlFor`: Makes `url_for` and similar helpers available. - `ActionController::Redirecting`: Support for `redirect_to`. -- `ActionController::Rendering`: Basic support for rendering. +- `AbstractController::Rendering` and `ActionController::ApiRendering`: Basic support for rendering. - `ActionController::Renderers::All`: Support for `render :json` and friends. - `ActionController::ConditionalGet`: Support for `stale?`. -- `ActionController::ForceSSL`: Support for `force_ssl`. -- `ActionController::RackDelegation`: Support for the `request` and `response` - methods returning `ActionDispatch::Request` and `ActionDispatch::Response` - objects. -- `ActionController::DataStreaming`: Support for `send_file` and `send_data`. -- `AbstractController::Callbacks`: Support for `before_filter` and friends. -- `ActionController::Instrumentation`: Support for the instrumentation - hooks defined by Action Controller (see [the instrumentation - guide](active_support_instrumentation.html#action-controller)). -- `ActionController::Rescue`: Support for `rescue_from`. - `ActionController::BasicImplicitRender`: Makes sure to return an empty response if there's not an explicit one. - `ActionController::StrongParameters`: Support for parameters white-listing in combination with Active Model mass assignment. +- `ActionController::ForceSSL`: Support for `force_ssl`. +- `ActionController::DataStreaming`: Support for `send_file` and `send_data`. +- `AbstractController::Callbacks`: Support for `before_action` and + similar helpers. +- `ActionController::Rescue`: Support for `rescue_from`. +- `ActionController::Instrumentation`: Support for the instrumentation + hooks defined by Action Controller (see [the instrumentation + guide](active_support_instrumentation.html#action-controller) for +more information regarding this). - `ActionController::ParamsWrapper`: Wraps the parameters hash into a nested hash so you don't have to specify root elements sending POST requests for instance. @@ -397,12 +397,12 @@ Some common modules you might want to add: - `AbstractController::Translation`: Support for the `l` and `t` localization and translation methods. -- `ActionController::HTTPAuthentication::Basic` (or `Digest` or `Token`): Support +- `ActionController::HttpAuthentication::Basic` (or `Digest` or `Token`): Support for basic, digest or token HTTP authentication. - `AbstractController::Layouts`: Support for layouts when rendering. - `ActionController::MimeResponds`: Support for `respond_to`. - `ActionController::Cookies`: Support for `cookies`, which includes support for signed and encrypted cookies. This requires the cookies middleware. -The best place to add a module is in your `ApplicationController` but you can +The best place to add a module is in your `ApplicationController`, but you can also add modules to individual controllers. diff --git a/guides/source/api_documentation_guidelines.md b/guides/source/api_documentation_guidelines.md index 46c9013087..cd208c2e13 100644 --- a/guides/source/api_documentation_guidelines.md +++ b/guides/source/api_documentation_guidelines.md @@ -20,7 +20,7 @@ The [Rails API documentation](http://api.rubyonrails.org) is generated with in the rails root directory, run `bundle install` and execute: ```bash - bundle exec rake rdoc + ./bin/rails rdoc ``` Resulting HTML files can be found in the ./doc/rdoc directory. @@ -84,6 +84,12 @@ English Please use American English (*color*, *center*, *modularize*, etc). See [a list of American and British English spelling differences here](http://en.wikipedia.org/wiki/American_and_British_English_spelling_differences). +Oxford Comma +------------ + +Please use the [Oxford comma](http://en.wikipedia.org/wiki/Serial_comma) +("red, white, and blue", instead of "red, white and blue"). + Example Code ------------ @@ -234,7 +240,7 @@ You can quickly test the RDoc output with the following command: ``` $ echo "+:to_param+" | rdoc --pipe -#=> <p><code>:to_param</code></p> +# => <p><code>:to_param</code></p> ``` ### Regular Font diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index 4a610e8458..439f2bef3a 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -45,7 +45,7 @@ gem 'coffee-rails' ``` Using the `--skip-sprockets` option will prevent Rails 4 from adding -`sass-rails` and `uglifier` to Gemfile, so if you later want to enable +`sass-rails` and `uglifier` to your Gemfile, so if you later want to enable the asset pipeline you will have to add those gems to your Gemfile. Also, creating an application with the `--skip-sprockets` option will generate a slightly different `config/application.rb` file, with a require statement @@ -66,7 +66,7 @@ config.assets.js_compressor = :uglifier ``` NOTE: The `sass-rails` gem is automatically used for CSS compression if included -in Gemfile and no `config.assets.css_compressor` option is set. +in the Gemfile and no `config.assets.css_compressor` option is set. ### Main Features @@ -169,7 +169,7 @@ directory. Files in this directory are served by the Sprockets middleware. Assets can still be placed in the `public` hierarchy. Any assets under `public` will be served as static files by the application or web server when -`config.serve_static_files` is set to true. You should use `app/assets` for +`config.public_file_server.enabled` is set to true. You should use `app/assets` for files that must undergo some pre-processing before they are served. In production, Rails precompiles these files to `public/assets` by default. The @@ -403,13 +403,13 @@ When using the asset pipeline, paths to assets must be re-written and underscored in Ruby) for the following asset classes: image, font, video, audio, JavaScript and stylesheet. -* `image-url("rails.png")` becomes `url(/assets/rails.png)` -* `image-path("rails.png")` becomes `"/assets/rails.png"`. +* `image-url("rails.png")` returns `url(/assets/rails.png)` +* `image-path("rails.png")` returns `"/assets/rails.png"` The more generic form can also be used: -* `asset-url("rails.png")` becomes `url(/assets/rails.png)` -* `asset-path("rails.png")` becomes `"/assets/rails.png"` +* `asset-url("rails.png")` returns `url(/assets/rails.png)` +* `asset-path("rails.png")` returns `"/assets/rails.png"` #### JavaScript/CoffeeScript and ERB @@ -662,7 +662,7 @@ generates something like this: rel="stylesheet" /> ``` -Note: with the Asset Pipeline the :cache and :concat options aren't used +NOTE: with the Asset Pipeline the `:cache` and `:concat` options aren't used anymore, delete these options from the `javascript_include_tag` and `stylesheet_link_tag`. @@ -676,7 +676,7 @@ content changes. ### Precompiling Assets -Rails comes bundled with a rake task to compile the asset manifests and other +Rails comes bundled with a task to compile the asset manifests and other files in the pipeline. Compiled assets are written to the location specified in `config.assets.prefix`. @@ -686,10 +686,10 @@ You can call this task on the server during deployment to create compiled versions of your assets directly on the server. See the next section for information on compiling locally. -The rake task is: +The task is: ```bash -$ RAILS_ENV=production bin/rake assets:precompile +$ RAILS_ENV=production bin/rails assets:precompile ``` Capistrano (v2.15.1 and above) includes a recipe to handle this in deployment. @@ -731,7 +731,7 @@ Rails.application.config.assets.precompile += ['admin.js', 'admin.css', 'swfObje NOTE. Always specify an expected compiled filename that ends with .js or .css, even if you want to add Sass or CoffeeScript files to the precompile array. -The rake task also generates a `manifest-md5hash.json` that contains a list with +The task also generates a `manifest-md5hash.json` that contains a list with all your assets and their respective fingerprints. This is used by the Rails helper methods to avoid handing the mapping requests back to Sprockets. A typical manifest file looks like: @@ -1021,7 +1021,7 @@ header](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9) is a W3C specification that describes how a request can be cached. When no CDN is used, a browser will use this information to cache contents. This is very helpful for assets that are not modified so that a browser does not need to re-download a -website's CSS or javascript on every request. Generally we want our Rails server +website's CSS or JavaScript on every request. Generally we want our Rails server to tell our CDN (and browser) that the asset is "public", that means any cache can store the request. Also we commonly want to set `max-age` which is how long the cache will store the object before invalidating the cache. The `max-age` @@ -1029,7 +1029,9 @@ value is set to seconds with a maximum possible value of `31536000` which is one year. You can do this in your rails application by setting ``` -config.static_cache_control = "public, max-age=31536000" +config.public_file_server.headers = { + 'Cache-Control' => 'public, max-age=31536000' +} ``` Now when your application serves an asset in production, the CDN will store the @@ -1175,19 +1177,14 @@ TIP: For further details have a look at the docs of your production web server: Assets Cache Store ------------------ -The default Rails cache store will be used by Sprockets to cache assets in -development and production. This can be changed by setting -`config.assets.cache_store`: - -```ruby -config.assets.cache_store = :memory_store -``` - -The options accepted by the assets cache store are the same as the application's -cache store. +By default, Sprockets caches assets in `tmp/cache/assets` in development +and production environments. This can be changed as follows: ```ruby -config.assets.cache_store = :memory_store, { size: 32.megabytes } +config.assets.configure do |env| + env.cache = ActiveSupport::Cache.lookup_store(:memory_store, + { size: 32.megabytes }) +end ``` To disable the assets cache store: @@ -1278,8 +1275,9 @@ config.assets.debug = true And in `production.rb`: ```ruby -# Choose the compressors to use (if any) config.assets.js_compressor = -# :uglifier config.assets.css_compressor = :yui +# Choose the compressors to use (if any) +config.assets.js_compressor = :uglifier +# config.assets.css_compressor = :yui # Don't fallback to assets pipeline if a precompiled asset is missed config.assets.compile = false @@ -1288,7 +1286,8 @@ config.assets.compile = false config.assets.digest = true # Precompile additional assets (application.js, application.css, and all -# non-JS/CSS are already added) config.assets.precompile += %w( search.js ) +# non-JS/CSS are already added) +# config.assets.precompile += %w( search.js ) ``` Rails 4 no longer sets default config values for Sprockets in `test.rb`, so @@ -1296,7 +1295,7 @@ Rails 4 no longer sets default config values for Sprockets in `test.rb`, so environment are: `config.assets.compile = true`, `config.assets.compress = false`, `config.assets.debug = false` and `config.assets.digest = false`. -The following should also be added to `Gemfile`: +The following should also be added to your `Gemfile`: ```ruby gem 'sass-rails', "~> 3.2.3" diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md index c0fa3cfd04..accce5a904 100644 --- a/guides/source/association_basics.md +++ b/guides/source/association_basics.md @@ -16,54 +16,54 @@ After reading this guide, you will know: Why Associations? ----------------- -Why do we need associations between models? Because they make common operations simpler and easier in your code. For example, consider a simple Rails application that includes a model for customers and a model for orders. Each customer can have many orders. Without associations, the model declarations would look like this: +In Rails, an _association_ is a connection between two Active Record models. Why do we need associations between models? Because they make common operations simpler and easier in your code. For example, consider a simple Rails application that includes a model for authors and a model for books. Each author can have many books. Without associations, the model declarations would look like this: ```ruby -class Customer < ActiveRecord::Base +class Author < ApplicationRecord end -class Order < ActiveRecord::Base +class Book < ApplicationRecord end ``` -Now, suppose we wanted to add a new order for an existing customer. We'd need to do something like this: +Now, suppose we wanted to add a new book for an existing author. We'd need to do something like this: ```ruby -@order = Order.create(order_date: Time.now, customer_id: @customer.id) +@book = Book.create(published_at: Time.now, author_id: @author.id) ``` -Or consider deleting a customer, and ensuring that all of its orders get deleted as well: +Or consider deleting an author, and ensuring that all of its books get deleted as well: ```ruby -@orders = Order.where(customer_id: @customer.id) -@orders.each do |order| - order.destroy +@books = Book.where(author_id: @author.id) +@books.each do |book| + book.destroy end -@customer.destroy +@author.destroy ``` -With Active Record associations, we can streamline these - and other - operations by declaratively telling Rails that there is a connection between the two models. Here's the revised code for setting up customers and orders: +With Active Record associations, we can streamline these - and other - operations by declaratively telling Rails that there is a connection between the two models. Here's the revised code for setting up authors and books: ```ruby -class Customer < ActiveRecord::Base - has_many :orders, dependent: :destroy +class Author < ApplicationRecord + has_many :books, dependent: :destroy end -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author end ``` -With this change, creating a new order for a particular customer is easier: +With this change, creating a new book for a particular author is easier: ```ruby -@order = @customer.orders.create(order_date: Time.now) +@book = @author.books.create(published_at: Time.now) ``` -Deleting a customer and all of its orders is *much* easier: +Deleting an author and all of its books is *much* easier: ```ruby -@customer.destroy +@author.destroy ``` To learn more about the different types of associations, read the next section of this guide. That's followed by some tips and tricks for working with associations, and then by a complete reference to the methods and options for associations in Rails. @@ -71,7 +71,7 @@ To learn more about the different types of associations, read the next section o The Types of Associations ------------------------- -In Rails, an _association_ is a connection between two Active Record models. Associations are implemented using macro-style calls, so that you can declaratively add features to your models. For example, by declaring that one model `belongs_to` another, you instruct Rails to maintain Primary Key-Foreign Key information between instances of the two models, and you also get a number of utility methods added to your model. Rails supports six types of associations: +Rails supports six types of associations: * `belongs_to` * `has_one` @@ -80,35 +80,37 @@ In Rails, an _association_ is a connection between two Active Record models. Ass * `has_one :through` * `has_and_belongs_to_many` +Associations are implemented using macro-style calls, so that you can declaratively add features to your models. For example, by declaring that one model `belongs_to` another, you instruct Rails to maintain [Primary Key](https://en.wikipedia.org/wiki/Unique_key)-[Foreign Key](https://en.wikipedia.org/wiki/Foreign_key) information between instances of the two models, and you also get a number of utility methods added to your model. + In the remainder of this guide, you'll learn how to declare and use the various forms of associations. But first, a quick introduction to the situations where each association type is appropriate. ### The `belongs_to` Association -A `belongs_to` association sets up a one-to-one connection with another model, such that each instance of the declaring model "belongs to" one instance of the other model. For example, if your application includes customers and orders, and each order can be assigned to exactly one customer, you'd declare the order model this way: +A `belongs_to` association sets up a one-to-one connection with another model, such that each instance of the declaring model "belongs to" one instance of the other model. For example, if your application includes authors and books, and each book can be assigned to exactly one author, you'd declare the book model this way: ```ruby -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author end ```  -NOTE: `belongs_to` associations _must_ use the singular term. If you used the pluralized form in the above example for the `customer` association in the `Order` model, you would be told that there was an "uninitialized constant Order::Customers". This is because Rails automatically infers the class name from the association name. If the association name is wrongly pluralized, then the inferred class will be wrongly pluralized too. +NOTE: `belongs_to` associations _must_ use the singular term. If you used the pluralized form in the above example for the `author` association in the `Book` model, you would be told that there was an "uninitialized constant Book::Authors". This is because Rails automatically infers the class name from the association name. If the association name is wrongly pluralized, then the inferred class will be wrongly pluralized too. The corresponding migration might look like this: ```ruby -class CreateOrders < ActiveRecord::Migration +class CreateBooks < ActiveRecord::Migration[5.0] def change - create_table :customers do |t| + create_table :authors do |t| t.string :name t.timestamps null: false end - create_table :orders do |t| - t.belongs_to :customer, index: true - t.datetime :order_date + create_table :books do |t| + t.belongs_to :author, index: true + t.datetime :published_at t.timestamps null: false end end @@ -120,7 +122,7 @@ end A `has_one` association also sets up a one-to-one connection with another model, but with somewhat different semantics (and consequences). This association indicates that each instance of a model contains or possesses one instance of another model. For example, if each supplier in your application has only one account, you'd declare the supplier model like this: ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account end ``` @@ -130,7 +132,7 @@ end The corresponding migration might look like this: ```ruby -class CreateSuppliers < ActiveRecord::Migration +class CreateSuppliers < ActiveRecord::Migration[5.0] def change create_table :suppliers do |t| t.string :name @@ -159,11 +161,11 @@ end ### The `has_many` Association -A `has_many` association indicates a one-to-many connection with another model. You'll often find this association on the "other side" of a `belongs_to` association. This association indicates that each instance of the model has zero or more instances of another model. For example, in an application containing customers and orders, the customer model could be declared like this: +A `has_many` association indicates a one-to-many connection with another model. You'll often find this association on the "other side" of a `belongs_to` association. This association indicates that each instance of the model has zero or more instances of another model. For example, in an application containing authors and books, the author model could be declared like this: ```ruby -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end ``` @@ -174,16 +176,16 @@ NOTE: The name of the other model is pluralized when declaring a `has_many` asso The corresponding migration might look like this: ```ruby -class CreateCustomers < ActiveRecord::Migration +class CreateAuthors < ActiveRecord::Migration[5.0] def change - create_table :customers do |t| + create_table :authors do |t| t.string :name t.timestamps null: false end - create_table :orders do |t| - t.belongs_to :customer, index: true - t.datetime :order_date + create_table :books do |t| + t.belongs_to :author, index: true + t.datetime :published_at t.timestamps null: false end end @@ -195,17 +197,17 @@ end A `has_many :through` association is often used to set up a many-to-many connection with another model. This association indicates that the declaring model can be matched with zero or more instances of another model by proceeding _through_ a third model. For example, consider a medical practice where patients make appointments to see physicians. The relevant association declarations could look like this: ```ruby -class Physician < ActiveRecord::Base +class Physician < ApplicationRecord has_many :appointments has_many :patients, through: :appointments end -class Appointment < ActiveRecord::Base +class Appointment < ApplicationRecord belongs_to :physician belongs_to :patient end -class Patient < ActiveRecord::Base +class Patient < ApplicationRecord has_many :appointments has_many :physicians, through: :appointments end @@ -216,7 +218,7 @@ end The corresponding migration might look like this: ```ruby -class CreateAppointments < ActiveRecord::Migration +class CreateAppointments < ActiveRecord::Migration[5.0] def change create_table :physicians do |t| t.string :name @@ -238,30 +240,32 @@ class CreateAppointments < ActiveRecord::Migration end ``` -The collection of join models can be managed via the API. For example, if you assign +The collection of join models can be managed via the [`has_many` association methods](#has-many-association-reference). +For example, if you assign: ```ruby physician.patients = patients ``` -new join models are created for newly associated objects, and if some are gone their rows are deleted. +Then new join models are automatically created for the newly associated objects. +If some that existed previously are now missing, then their join rows are automatically deleted. WARNING: Automatic deletion of join models is direct, no destroy callbacks are triggered. The `has_many :through` association is also useful for setting up "shortcuts" through nested `has_many` associations. For example, if a document has many sections, and a section has many paragraphs, you may sometimes want to get a simple collection of all paragraphs in the document. You could set that up this way: ```ruby -class Document < ActiveRecord::Base +class Document < ApplicationRecord has_many :sections has_many :paragraphs, through: :sections end -class Section < ActiveRecord::Base +class Section < ApplicationRecord belongs_to :document has_many :paragraphs end -class Paragraph < ActiveRecord::Base +class Paragraph < ApplicationRecord belongs_to :section end ``` @@ -280,17 +284,17 @@ For example, if each supplier has one account, and each account is associated wi supplier model could look like this: ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account has_one :account_history, through: :account end -class Account < ActiveRecord::Base +class Account < ApplicationRecord belongs_to :supplier has_one :account_history end -class AccountHistory < ActiveRecord::Base +class AccountHistory < ApplicationRecord belongs_to :account end ``` @@ -300,7 +304,7 @@ end The corresponding migration might look like this: ```ruby -class CreateAccountHistories < ActiveRecord::Migration +class CreateAccountHistories < ActiveRecord::Migration[5.0] def change create_table :suppliers do |t| t.string :name @@ -327,11 +331,11 @@ end A `has_and_belongs_to_many` association creates a direct many-to-many connection with another model, with no intervening model. For example, if your application includes assemblies and parts, with each assembly having many parts and each part appearing in many assemblies, you could declare the models this way: ```ruby -class Assembly < ActiveRecord::Base +class Assembly < ApplicationRecord has_and_belongs_to_many :parts end -class Part < ActiveRecord::Base +class Part < ApplicationRecord has_and_belongs_to_many :assemblies end ``` @@ -341,7 +345,7 @@ end The corresponding migration might look like this: ```ruby -class CreateAssembliesAndParts < ActiveRecord::Migration +class CreateAssembliesAndParts < ActiveRecord::Migration[5.0] def change create_table :assemblies do |t| t.string :name @@ -368,11 +372,11 @@ If you want to set up a one-to-one relationship between two models, you'll need The distinction is in where you place the foreign key (it goes on the table for the class declaring the `belongs_to` association), but you should give some thought to the actual meaning of the data as well. The `has_one` relationship says that one of something is yours - that is, that something points back to you. For example, it makes more sense to say that a supplier owns an account than that an account owns a supplier. This suggests that the correct relationships are like this: ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account end -class Account < ActiveRecord::Base +class Account < ApplicationRecord belongs_to :supplier end ``` @@ -380,7 +384,7 @@ end The corresponding migration might look like this: ```ruby -class CreateSuppliers < ActiveRecord::Migration +class CreateSuppliers < ActiveRecord::Migration[5.0] def change create_table :suppliers do |t| t.string :name @@ -405,11 +409,11 @@ NOTE: Using `t.integer :supplier_id` makes the foreign key naming obvious and ex Rails offers two different ways to declare a many-to-many relationship between models. The simpler way is to use `has_and_belongs_to_many`, which allows you to make the association directly: ```ruby -class Assembly < ActiveRecord::Base +class Assembly < ApplicationRecord has_and_belongs_to_many :parts end -class Part < ActiveRecord::Base +class Part < ApplicationRecord has_and_belongs_to_many :assemblies end ``` @@ -417,17 +421,17 @@ end The second way to declare a many-to-many relationship is to use `has_many :through`. This makes the association indirectly, through a join model: ```ruby -class Assembly < ActiveRecord::Base +class Assembly < ApplicationRecord has_many :manifests has_many :parts, through: :manifests end -class Manifest < ActiveRecord::Base +class Manifest < ApplicationRecord belongs_to :assembly belongs_to :part end -class Part < ActiveRecord::Base +class Part < ApplicationRecord has_many :manifests has_many :assemblies, through: :manifests end @@ -442,15 +446,15 @@ You should use `has_many :through` if you need validations, callbacks or extra a A slightly more advanced twist on associations is the _polymorphic association_. With polymorphic associations, a model can belong to more than one other model, on a single association. For example, you might have a picture model that belongs to either an employee model or a product model. Here's how this could be declared: ```ruby -class Picture < ActiveRecord::Base +class Picture < ApplicationRecord belongs_to :imageable, polymorphic: true end -class Employee < ActiveRecord::Base +class Employee < ApplicationRecord has_many :pictures, as: :imageable end -class Product < ActiveRecord::Base +class Product < ApplicationRecord has_many :pictures, as: :imageable end ``` @@ -462,7 +466,7 @@ Similarly, you can retrieve `@product.pictures`. If you have an instance of the `Picture` model, you can get to its parent via `@picture.imageable`. To make this work, you need to declare both a foreign key column and a type column in the model that declares the polymorphic interface: ```ruby -class CreatePictures < ActiveRecord::Migration +class CreatePictures < ActiveRecord::Migration[5.0] def change create_table :pictures do |t| t.string :name @@ -479,7 +483,7 @@ end This migration can be simplified by using the `t.references` form: ```ruby -class CreatePictures < ActiveRecord::Migration +class CreatePictures < ActiveRecord::Migration[5.0] def change create_table :pictures do |t| t.string :name @@ -497,7 +501,7 @@ end In designing a data model, you will sometimes find a model that should have a relation to itself. For example, you may want to store all employees in a single database model, but be able to trace relationships such as between manager and subordinates. This situation can be modeled with self-joining associations: ```ruby -class Employee < ActiveRecord::Base +class Employee < ApplicationRecord has_many :subordinates, class_name: "Employee", foreign_key: "manager_id" @@ -510,7 +514,7 @@ With this setup, you can retrieve `@employee.subordinates` and `@employee.manage In your migrations/schema, you will add a references column to the model itself. ```ruby -class CreateEmployees < ActiveRecord::Migration +class CreateEmployees < ActiveRecord::Migration[5.0] def change create_table :employees do |t| t.references :manager, index: true @@ -536,17 +540,17 @@ Here are a few things you should know to make efficient use of Active Record ass All of the association methods are built around caching, which keeps the result of the most recent query available for further operations. The cache is even shared across methods. For example: ```ruby -customer.orders # retrieves orders from the database -customer.orders.size # uses the cached copy of orders -customer.orders.empty? # uses the cached copy of orders +author.books # retrieves books from the database +author.books.size # uses the cached copy of books +author.books.empty? # uses the cached copy of books ``` But what if you want to reload the cache, because data might have been changed by some other part of the application? Just pass `true` to the association call: ```ruby -customer.orders # retrieves orders from the database -customer.orders.size # uses the cached copy of orders -customer.orders(true).empty? # discards the cached copy of orders +author.books # retrieves books from the database +author.books.size # uses the cached copy of books +author.books(true).empty? # discards the cached copy of books # and goes back to the database ``` @@ -563,23 +567,23 @@ Associations are extremely useful, but they are not magic. You are responsible f When you declare a `belongs_to` association, you need to create foreign keys as appropriate. For example, consider this model: ```ruby -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author end ``` -This declaration needs to be backed up by the proper foreign key declaration on the orders table: +This declaration needs to be backed up by the proper foreign key declaration on the books table: ```ruby -class CreateOrders < ActiveRecord::Migration +class CreateBooks < ActiveRecord::Migration[5.0] def change - create_table :orders do |t| - t.datetime :order_date - t.string :order_number - t.integer :customer_id + create_table :books do |t| + t.datetime :published_at + t.string :book_number + t.integer :author_id end - add_index :orders, :customer_id + add_index :books, :author_id end end ``` @@ -588,18 +592,18 @@ If you create an association some time after you build the underlying model, you #### Creating Join Tables for `has_and_belongs_to_many` Associations -If you create a `has_and_belongs_to_many` association, you need to explicitly create the joining table. Unless the name of the join table is explicitly specified by using the `:join_table` option, Active Record creates the name by using the lexical order of the class names. So a join between customer and order models will give the default join table name of "customers_orders" because "c" outranks "o" in lexical ordering. +If you create a `has_and_belongs_to_many` association, you need to explicitly create the joining table. Unless the name of the join table is explicitly specified by using the `:join_table` option, Active Record creates the name by using the lexical book of the class names. So a join between author and book models will give the default join table name of "authors_books" because "a" outranks "b" in lexical ordering. WARNING: The precedence between model names is calculated using the `<=>` operator for `String`. This means that if the strings are of different lengths, and the strings are equal when compared up to the shortest length, then the longer string is considered of higher lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers" to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes", but it in fact generates a join table name of "paper_boxes_papers" (because the underscore '\_' is lexicographically _less_ than 's' in common encodings). Whatever the name, you must manually generate the join table with an appropriate migration. For example, consider these associations: ```ruby -class Assembly < ActiveRecord::Base +class Assembly < ApplicationRecord has_and_belongs_to_many :parts end -class Part < ActiveRecord::Base +class Part < ApplicationRecord has_and_belongs_to_many :assemblies end ``` @@ -607,7 +611,7 @@ end These need to be backed up by a migration to create the `assemblies_parts` table. This table should be created without a primary key: ```ruby -class CreateAssembliesPartsJoinTable < ActiveRecord::Migration +class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[5.0] def change create_table :assemblies_parts, id: false do |t| t.integer :assembly_id @@ -622,6 +626,19 @@ end We pass `id: false` to `create_table` because that table does not represent a model. That's required for the association to work properly. If you observe any strange behavior in a `has_and_belongs_to_many` association like mangled model IDs, or exceptions about conflicting IDs, chances are you forgot that bit. +You can also use the method `create_join_table` + +```ruby +class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[5.0] + def change + create_join_table :assemblies, :parts do |t| + t.index :assembly_id + t.index :part_id + end + end +end +``` + ### Controlling Association Scope By default, associations look for objects only within the current module's scope. This can be important when you declare Active Record models within a module. For example: @@ -629,11 +646,11 @@ By default, associations look for objects only within the current module's scope ```ruby module MyApplication module Business - class Supplier < ActiveRecord::Base + class Supplier < ApplicationRecord has_one :account end - class Account < ActiveRecord::Base + class Account < ApplicationRecord belongs_to :supplier end end @@ -645,13 +662,13 @@ This will work fine, because both the `Supplier` and the `Account` class are def ```ruby module MyApplication module Business - class Supplier < ActiveRecord::Base + class Supplier < ApplicationRecord has_one :account end end module Billing - class Account < ActiveRecord::Base + class Account < ApplicationRecord belongs_to :supplier end end @@ -663,14 +680,14 @@ To associate a model with a model in a different namespace, you must specify the ```ruby module MyApplication module Business - class Supplier < ActiveRecord::Base + class Supplier < ApplicationRecord has_one :account, class_name: "MyApplication::Billing::Account" end end module Billing - class Account < ActiveRecord::Base + class Account < ApplicationRecord belongs_to :supplier, class_name: "MyApplication::Business::Supplier" end @@ -683,45 +700,45 @@ end It's normal for associations to work in two directions, requiring declaration on two different models: ```ruby -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author end ``` By default, Active Record doesn't know about the connection between these associations. This can lead to two copies of an object getting out of sync: ```ruby -c = Customer.first -o = c.orders.first -c.first_name == o.customer.first_name # => true -c.first_name = 'Manny' -c.first_name == o.customer.first_name # => false +a = Author.first +b = c.books.first +a.first_name == b.author.first_name # => true +a.first_name = 'Manny' +a.first_name == b.author.first_name # => false ``` -This happens because `c` and `o.customer` are two different in-memory representations of the same data, and neither one is automatically refreshed from changes to the other. Active Record provides the `:inverse_of` option so that you can inform it of these relations: +This happens because `a` and `b.author` are two different in-memory representations of the same data, and neither one is automatically refreshed from changes to the other. Active Record provides the `:inverse_of` option so that you can inform it of these relations: ```ruby -class Customer < ActiveRecord::Base - has_many :orders, inverse_of: :customer +class Author < ApplicationRecord + has_many :books, inverse_of: :author end -class Order < ActiveRecord::Base - belongs_to :customer, inverse_of: :orders +class book < ApplicationRecord + belongs_to :author, inverse_of: :books end ``` -With these changes, Active Record will only load one copy of the customer object, preventing inconsistencies and making your application more efficient: +With these changes, Active Record will only load one copy of the author object, preventing inconsistencies and making your application more efficient: ```ruby -c = Customer.first -o = c.orders.first -c.first_name == o.customer.first_name # => true -c.first_name = 'Manny' -c.first_name == o.customer.first_name # => true +a = author.first +b = c.books.first +a.first_name == b.author.first_name # => true +a.first_name = 'Manny' +a.first_name == b.author.first_name # => true ``` There are a few limitations to `inverse_of` support: @@ -755,7 +772,7 @@ The `belongs_to` association creates a one-to-one match with another model. In d When you declare a `belongs_to` association, the declaring class automatically gains five methods related to the association: -* `association(force_reload = false)` +* `association` * `association=(associate)` * `build_association(attributes = {})` * `create_association(attributes = {})` @@ -764,39 +781,43 @@ When you declare a `belongs_to` association, the declaring class automatically g In all of these methods, `association` is replaced with the symbol passed as the first argument to `belongs_to`. For example, given the declaration: ```ruby -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author end ``` -Each instance of the `Order` model will have these methods: +Each instance of the `Book` model will have these methods: ```ruby -customer -customer= -build_customer -create_customer -create_customer! +author +author= +build_author +create_author +create_author! ``` NOTE: When initializing a new `has_one` or `belongs_to` association you must use the `build_` prefix to build the association, rather than the `association.build` method that would be used for `has_many` or `has_and_belongs_to_many` associations. To create one, use the `create_` prefix. -##### `association(force_reload = false)` +##### `association` The `association` method returns the associated object, if any. If no associated object is found, it returns `nil`. ```ruby -@customer = @order.customer +@author = @book.author ``` -If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), pass `true` as the `force_reload` argument. +If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), call `#reload` on the parent object. + +```ruby +@author = @book.reload.author +``` ##### `association=(associate)` The `association=` method assigns an associated object to this object. Behind the scenes, this means extracting the primary key from the associated object and setting this object's foreign key to the same value. ```ruby -@order.customer = @customer +@book.author = @author ``` ##### `build_association(attributes = {})` @@ -804,8 +825,8 @@ The `association=` method assigns an associated object to this object. Behind th The `build_association` method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through this object's foreign key will be set, but the associated object will _not_ yet be saved. ```ruby -@customer = @order.build_customer(customer_number: 123, - customer_name: "John Doe") +@author = @book.build_author(author_number: 123, + author_name: "John Doe") ``` ##### `create_association(attributes = {})` @@ -813,8 +834,8 @@ The `build_association` method returns a new object of the associated type. This The `create_association` method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through this object's foreign key will be set, and, once it passes all of the validations specified on the associated model, the associated object _will_ be saved. ```ruby -@customer = @order.create_customer(customer_number: 123, - customer_name: "John Doe") +@author = @book.create_author(author_number: 123, + author_name: "John Doe") ``` ##### `create_association!(attributes = {})` @@ -827,8 +848,8 @@ Does the same as `create_association` above, but raises `ActiveRecord::RecordInv While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `belongs_to` association reference. Such customizations can easily be accomplished by passing options and scope blocks when you create the association. For example, this association uses two such options: ```ruby -class Order < ActiveRecord::Base - belongs_to :customer, dependent: :destroy, +class Book < ApplicationRecord + belongs_to :author, dependent: :destroy, counter_cache: true end ``` @@ -853,11 +874,11 @@ If you set the `:autosave` option to `true`, Rails will save any loaded members ##### `:class_name` -If the name of the other model cannot be derived from the association name, you can use the `:class_name` option to supply the model name. For example, if an order belongs to a customer, but the actual name of the model containing customers is `Patron`, you'd set things up this way: +If the name of the other model cannot be derived from the association name, you can use the `:class_name` option to supply the model name. For example, if a book belongs to an author, but the actual name of the model containing authors is `Patron`, you'd set things up this way: ```ruby -class Order < ActiveRecord::Base - belongs_to :customer, class_name: "Patron" +class Book < ApplicationRecord + belongs_to :author, class_name: "Patron" end ``` @@ -866,22 +887,22 @@ end The `:counter_cache` option can be used to make finding the number of belonging objects more efficient. Consider these models: ```ruby -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author end -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end ``` -With these declarations, asking for the value of `@customer.orders.size` requires making a call to the database to perform a `COUNT(*)` query. To avoid this call, you can add a counter cache to the _belonging_ model: +With these declarations, asking for the value of `@author.books.size` requires making a call to the database to perform a `COUNT(*)` query. To avoid this call, you can add a counter cache to the _belonging_ model: ```ruby -class Order < ActiveRecord::Base - belongs_to :customer, counter_cache: true +class Book < ApplicationRecord + belongs_to :author, counter_cache: true end -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end ``` @@ -890,18 +911,18 @@ With this declaration, Rails will keep the cache value up to date, and then retu Although the `:counter_cache` option is specified on the model that includes the `belongs_to` declaration, the actual column must be added to the _associated_ (`has_many`) model. In the case above, you would need to add a -column named `orders_count` to the `Customer` model. +column named `books_count` to the `Author` model. You can override the default column name by specifying a custom column name in the `counter_cache` declaration instead of `true`. For example, to use -`count_of_orders` instead of `orders_count`: +`count_of_books` instead of `books_count`: ```ruby -class Order < ActiveRecord::Base - belongs_to :customer, counter_cache: :count_of_orders +class Book < ApplicationRecord + belongs_to :author, counter_cache: :count_of_books end -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end ``` @@ -915,8 +936,11 @@ If you set the `:dependent` option to: * `:destroy`, when the object is destroyed, `destroy` will be called on its associated objects. -* `:delete`, when the object is destroyed, all its associated objects will be +* `:delete_all`, when the object is destroyed, all its associated objects will be deleted directly from the database without calling their `destroy` method. +* `:nullify`, causes the foreign key to be set to `NULL`. Callbacks are not executed. +* `:restrict_with_exception`, causes an exception to be raised if there is an associated record +* `:restrict_with_error`, causes an error to be added to the owner if there is an associated object WARNING: You should not specify this option on a `belongs_to` association that is connected with a `has_many` association on the other class. Doing so can lead to orphaned records in your database. @@ -925,8 +949,8 @@ WARNING: You should not specify this option on a `belongs_to` association that i By convention, Rails assumes that the column used to hold the foreign key on this model is the name of the association with the suffix `_id` added. The `:foreign_key` option lets you set the name of the foreign key directly: ```ruby -class Order < ActiveRecord::Base - belongs_to :customer, class_name: "Patron", +class Book < ApplicationRecord + belongs_to :author, class_name: "Patron", foreign_key: "patron_id" end ``` @@ -941,11 +965,11 @@ of its tables. The `:primary_key` option allows you to specify a different colum For example, given we have a `users` table with `guid` as the primary key. If we want a separate `todos` table to hold the foreign key `user_id` in the `guid` column, then we can use `primary_key` to achieve this like so: ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord self.primary_key = 'guid' # primary key is guid and not id end -class Todo < ActiveRecord::Base +class Todo < ApplicationRecord belongs_to :user, primary_key: 'guid' end ``` @@ -958,12 +982,12 @@ When we execute `@user.todos.create` then the `@todo` record will have its The `:inverse_of` option specifies the name of the `has_many` or `has_one` association that is the inverse of this association. Does not work in combination with the `:polymorphic` options. ```ruby -class Customer < ActiveRecord::Base - has_many :orders, inverse_of: :customer +class Author < ApplicationRecord + has_many :books, inverse_of: :author end -class Order < ActiveRecord::Base - belongs_to :customer, inverse_of: :orders +class Book < ApplicationRecord + belongs_to :author, inverse_of: :books end ``` @@ -976,20 +1000,20 @@ Passing `true` to the `:polymorphic` option indicates that this is a polymorphic If you set the `:touch` option to `true`, then the `updated_at` or `updated_on` timestamp on the associated object will be set to the current time whenever this object is saved or destroyed: ```ruby -class Order < ActiveRecord::Base - belongs_to :customer, touch: true +class Book < ApplicationRecord + belongs_to :author, touch: true end -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end ``` -In this case, saving or destroying an order will update the timestamp on the associated customer. You can also specify a particular timestamp attribute to update: +In this case, saving or destroying an book will update the timestamp on the associated author. You can also specify a particular timestamp attribute to update: ```ruby -class Order < ActiveRecord::Base - belongs_to :customer, touch: :orders_updated_at +class Book < ApplicationRecord + belongs_to :author, touch: :books_updated_at end ``` @@ -1007,8 +1031,8 @@ object won't be validated. By default, this option is set to `false`. There may be times when you wish to customize the query used by `belongs_to`. Such customizations can be achieved via a scope block. For example: ```ruby -class Order < ActiveRecord::Base - belongs_to :customer, -> { where active: true }, +class Book < ApplicationRecord + belongs_to :author, -> { where active: true }, dependent: :destroy end ``` @@ -1025,8 +1049,8 @@ You can use any of the standard [querying methods](active_record_querying.html) The `where` method lets you specify the conditions that the associated object must meet. ```ruby -class Order < ActiveRecord::Base - belongs_to :customer, -> { where active: true } +class book < ApplicationRecord + belongs_to :author, -> { where active: true } end ``` @@ -1035,38 +1059,38 @@ end You can use the `includes` method to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models: ```ruby -class LineItem < ActiveRecord::Base - belongs_to :order +class LineItem < ApplicationRecord + belongs_to :book end -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author has_many :line_items end -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end ``` -If you frequently retrieve customers directly from line items (`@line_item.order.customer`), then you can make your code somewhat more efficient by including customers in the association from line items to orders: +If you frequently retrieve authors directly from line items (`@line_item.book.author`), then you can make your code somewhat more efficient by including authors in the association from line items to books: ```ruby -class LineItem < ActiveRecord::Base - belongs_to :order, -> { includes :customer } +class LineItem < ApplicationRecord + belongs_to :book, -> { includes :author } end -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author has_many :line_items end -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end ``` -NOTE: There's no need to use `includes` for immediate associations - that is, if you have `Order belongs_to :customer`, then the customer is eager-loaded automatically when it's needed. +NOTE: There's no need to use `includes` for immediate associations - that is, if you have `Book belongs_to :author`, then the author is eager-loaded automatically when it's needed. ##### `readonly` @@ -1083,8 +1107,8 @@ TIP: If you use the `select` method on a `belongs_to` association, you should al You can see if any associated objects exist by using the `association.nil?` method: ```ruby -if @order.customer.nil? - @msg = "No customer found for this order" +if @book.author.nil? + @msg = "No author found for this book" end ``` @@ -1100,7 +1124,7 @@ The `has_one` association creates a one-to-one match with another model. In data When you declare a `has_one` association, the declaring class automatically gains five methods related to the association: -* `association(force_reload = false)` +* `association` * `association=(associate)` * `build_association(attributes = {})` * `create_association(attributes = {})` @@ -1109,7 +1133,7 @@ When you declare a `has_one` association, the declaring class automatically gain In all of these methods, `association` is replaced with the symbol passed as the first argument to `has_one`. For example, given the declaration: ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account end ``` @@ -1126,7 +1150,7 @@ create_account! NOTE: When initializing a new `has_one` or `belongs_to` association you must use the `build_` prefix to build the association, rather than the `association.build` method that would be used for `has_many` or `has_and_belongs_to_many` associations. To create one, use the `create_` prefix. -##### `association(force_reload = false)` +##### `association` The `association` method returns the associated object, if any. If no associated object is found, it returns `nil`. @@ -1134,7 +1158,11 @@ The `association` method returns the associated object, if any. If no associated @account = @supplier.account ``` -If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), pass `true` as the `force_reload` argument. +If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), call `#reload` on the parent object. + +```ruby +@account = @supplier.reload.account +``` ##### `association=(associate)` @@ -1169,7 +1197,7 @@ Does the same as `create_association` above, but raises `ActiveRecord::RecordInv While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `has_one` association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this association uses two such options: ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account, class_name: "Billing", dependent: :nullify end ``` @@ -1201,7 +1229,7 @@ If you set the `:autosave` option to `true`, Rails will save any loaded members If the name of the other model cannot be derived from the association name, you can use the `:class_name` option to supply the model name. For example, if a supplier has an account, but the actual name of the model containing accounts is `Billing`, you'd set things up this way: ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account, class_name: "Billing" end ``` @@ -1227,7 +1255,7 @@ unallowed `NULL` value. 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: ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account, foreign_key: "supp_id" end ``` @@ -1239,11 +1267,11 @@ TIP: In any case, Rails will not create foreign key columns for you. You need to The `:inverse_of` option specifies the name of the `belongs_to` association that is the inverse of this association. Does not work in combination with the `:through` or `:as` options. ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account, inverse_of: :supplier end -class Account < ActiveRecord::Base +class Account < ApplicationRecord belongs_to :supplier, inverse_of: :account end ``` @@ -1273,7 +1301,7 @@ If you set the `:validate` option to `true`, then associated objects will be val There may be times when you wish to customize the query used by `has_one`. Such customizations can be achieved via a scope block. For example: ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account, -> { where active: true } end ``` @@ -1290,7 +1318,7 @@ You can use any of the standard [querying methods](active_record_querying.html) The `where` method lets you specify the conditions that the associated object must meet. ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account, -> { where "confirmed = 1" } end ``` @@ -1300,16 +1328,16 @@ end You can use the `includes` method to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models: ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account end -class Account < ActiveRecord::Base +class Account < ApplicationRecord belongs_to :supplier belongs_to :representative end -class Representative < ActiveRecord::Base +class Representative < ApplicationRecord has_many :accounts end ``` @@ -1317,16 +1345,16 @@ end If you frequently retrieve representatives directly from suppliers (`@supplier.account.representative`), then you can make your code somewhat more efficient by including representatives in the association from suppliers to accounts: ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account, -> { includes :representative } end -class Account < ActiveRecord::Base +class Account < ApplicationRecord belongs_to :supplier belongs_to :representative end -class Representative < ActiveRecord::Base +class Representative < ApplicationRecord has_many :accounts end ``` @@ -1367,7 +1395,7 @@ The `has_many` association creates a one-to-many relationship with another model When you declare a `has_many` association, the declaring class automatically gains 16 methods related to the association: -* `collection(force_reload = false)` +* `collection` * `collection<<(object, ...)` * `collection.delete(object, ...)` * `collection.destroy(object, ...)` @@ -1387,38 +1415,38 @@ When you declare a `has_many` association, the declaring class automatically gai In all of these methods, `collection` is replaced with the symbol passed as the first argument to `has_many`, and `collection_singular` is replaced with the singularized version of that symbol. For example, given the declaration: ```ruby -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end ``` -Each instance of the `Customer` model will have these methods: +Each instance of the `Author` model will have these methods: ```ruby -orders(force_reload = false) -orders<<(object, ...) -orders.delete(object, ...) -orders.destroy(object, ...) -orders=(objects) -order_ids -order_ids=(ids) -orders.clear -orders.empty? -orders.size -orders.find(...) -orders.where(...) -orders.exists?(...) -orders.build(attributes = {}, ...) -orders.create(attributes = {}) -orders.create!(attributes = {}) +books +books<<(object, ...) +books.delete(object, ...) +books.destroy(object, ...) +books=(objects) +book_ids +book_ids=(ids) +books.clear +books.empty? +books.size +books.find(...) +books.where(...) +books.exists?(...) +books.build(attributes = {}, ...) +books.create(attributes = {}) +books.create!(attributes = {}) ``` -##### `collection(force_reload = false)` +##### `collection` The `collection` method returns an array of all of the associated objects. If there are no associated objects, it returns an empty array. ```ruby -@orders = @customer.orders +@books = @author.books ``` ##### `collection<<(object, ...)` @@ -1426,7 +1454,7 @@ The `collection` method returns an array of all of the associated objects. If th The `collection<<` method adds one or more objects to the collection by setting their foreign keys to the primary key of the calling model. ```ruby -@customer.orders << @order1 +@author.books << @book1 ``` ##### `collection.delete(object, ...)` @@ -1434,7 +1462,7 @@ The `collection<<` method adds one or more objects to the collection by setting The `collection.delete` method removes one or more objects from the collection by setting their foreign keys to `NULL`. ```ruby -@customer.orders.delete(@order1) +@author.books.delete(@book1) ``` WARNING: Additionally, objects will be destroyed if they're associated with `dependent: :destroy`, and deleted if they're associated with `dependent: :delete_all`. @@ -1444,7 +1472,7 @@ WARNING: Additionally, objects will be destroyed if they're associated with `dep The `collection.destroy` method removes one or more objects from the collection by running `destroy` on each object. ```ruby -@customer.orders.destroy(@order1) +@author.books.destroy(@book1) ``` WARNING: Objects will _always_ be removed from the database, ignoring the `:dependent` option. @@ -1458,7 +1486,7 @@ The `collection=` method makes the collection contain only the supplied objects, The `collection_singular_ids` method returns an array of the ids of the objects in the collection. ```ruby -@order_ids = @customer.order_ids +@book_ids = @author.book_ids ``` ##### `collection_singular_ids=(ids)` @@ -1470,7 +1498,7 @@ The `collection_singular_ids=` method makes the collection contain only the obje The `collection.clear` method removes all objects from the collection according to the strategy specified by the `dependent` option. If no option is given, it follows the default strategy. The default strategy for `has_many :through` associations is `delete_all`, and for `has_many` associations is to set the foreign keys to `NULL`. ```ruby -@customer.orders.clear +@author.books.clear ``` WARNING: Objects will be deleted if they're associated with `dependent: :destroy`, @@ -1481,8 +1509,8 @@ just like `dependent: :delete_all`. The `collection.empty?` method returns `true` if the collection does not contain any associated objects. ```erb -<% if @customer.orders.empty? %> - No Orders Found +<% if @author.books.empty? %> + No Books Found <% end %> ``` @@ -1491,7 +1519,7 @@ The `collection.empty?` method returns `true` if the collection does not contain The `collection.size` method returns the number of objects in the collection. ```ruby -@order_count = @customer.orders.size +@book_count = @author.books.size ``` ##### `collection.find(...)` @@ -1499,7 +1527,7 @@ The `collection.size` method returns the number of objects in the collection. The `collection.find` method finds objects within the collection. It uses the same syntax and options as `ActiveRecord::Base.find`. ```ruby -@open_orders = @customer.orders.find(1) +@available_books = @author.books.find(1) ``` ##### `collection.where(...)` @@ -1507,8 +1535,8 @@ The `collection.find` method finds objects within the collection. It uses the sa The `collection.where` method finds objects within the collection based on the conditions supplied but the objects are loaded lazily meaning that the database is queried only when the object(s) are accessed. ```ruby -@open_orders = @customer.orders.where(open: true) # No query yet -@open_order = @open_orders.first # Now the database will be queried +@available_books = @author.books.where(available: true) # No query yet +@available_book = @available_books.first # Now the database will be queried ``` ##### `collection.exists?(...)` @@ -1522,12 +1550,12 @@ conditions exists in the collection. It uses the same syntax and options as The `collection.build` method returns a single or array of new objects of the associated type. The object(s) will be instantiated from the passed attributes, and the link through their foreign key will be created, but the associated objects will _not_ yet be saved. ```ruby -@order = @customer.orders.build(order_date: Time.now, - order_number: "A12345") +@book = @author.books.build(published_at: Time.now, + book_number: "A12345") -@orders = @customer.orders.build([ - { order_date: Time.now, order_number: "A12346" }, - { order_date: Time.now, order_number: "A12347" } +@books = @author.books.build([ + { published_at: Time.now, book_number: "A12346" }, + { published_at: Time.now, book_number: "A12347" } ]) ``` @@ -1536,12 +1564,12 @@ The `collection.build` method returns a single or array of new objects of the as The `collection.create` method returns a single or array of new objects of the associated type. The object(s) will be instantiated from the passed attributes, the link through its foreign key will be created, and, once it passes all of the validations specified on the associated model, the associated object _will_ be saved. ```ruby -@order = @customer.orders.create(order_date: Time.now, - order_number: "A12345") +@book = @author.books.create(published_at: Time.now, + book_number: "A12345") -@orders = @customer.orders.create([ - { order_date: Time.now, order_number: "A12346" }, - { order_date: Time.now, order_number: "A12347" } +@books = @author.books.create([ + { published_at: Time.now, book_number: "A12346" }, + { published_at: Time.now, book_number: "A12347" } ]) ``` @@ -1554,8 +1582,8 @@ Does the same as `collection.create` above, but raises `ActiveRecord::RecordInva While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `has_many` association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this association uses two such options: ```ruby -class Customer < ActiveRecord::Base - has_many :orders, dependent: :delete_all, validate: false +class Author < ApplicationRecord + has_many :books, dependent: :delete_all, validate: false end ``` @@ -1584,11 +1612,11 @@ If you set the `:autosave` option to `true`, Rails will save any loaded members ##### `:class_name` -If the name of the other model cannot be derived from the association name, you can use the `:class_name` option to supply the model name. For example, if a customer has many orders, but the actual name of the model containing orders is `Transaction`, you'd set things up this way: +If the name of the other model cannot be derived from the association name, you can use the `:class_name` option to supply the model name. For example, if an author has many books, but the actual name of the model containing books is `Transaction`, you'd set things up this way: ```ruby -class Customer < ActiveRecord::Base - has_many :orders, class_name: "Transaction" +class Author < ApplicationRecord + has_many :books, class_name: "Transaction" end ``` @@ -1611,8 +1639,8 @@ Controls what happens to the associated objects when their owner is destroyed: 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: ```ruby -class Customer < ActiveRecord::Base - has_many :orders, foreign_key: "cust_id" +class Author < ApplicationRecord + has_many :books, foreign_key: "cust_id" end ``` @@ -1623,12 +1651,12 @@ TIP: In any case, Rails will not create foreign key columns for you. You need to The `:inverse_of` option specifies the name of the `belongs_to` association that is the inverse of this association. Does not work in combination with the `:through` or `:as` options. ```ruby -class Customer < ActiveRecord::Base - has_many :orders, inverse_of: :customer +class Author < ApplicationRecord + has_many :books, inverse_of: :author end -class Order < ActiveRecord::Base - belongs_to :customer, inverse_of: :orders +class Book < ApplicationRecord + belongs_to :author, inverse_of: :books end ``` @@ -1642,7 +1670,7 @@ hold the `guid` column value as the foreign key and not `id` value. This can be achieved like this: ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord has_many :todos, primary_key: :guid end ``` @@ -1672,8 +1700,8 @@ If you set the `:validate` option to `false`, then associated objects will not b There may be times when you wish to customize the query used by `has_many`. Such customizations can be achieved via a scope block. For example: ```ruby -class Customer < ActiveRecord::Base - has_many :orders, -> { where processed: true } +class Author < ApplicationRecord + has_many :books, -> { where processed: true } end ``` @@ -1695,22 +1723,22 @@ You can use any of the standard [querying methods](active_record_querying.html) The `where` method lets you specify the conditions that the associated object must meet. ```ruby -class Customer < ActiveRecord::Base - has_many :confirmed_orders, -> { where "confirmed = 1" }, - class_name: "Order" +class Author < ApplicationRecord + has_many :confirmed_books, -> { where "confirmed = 1" }, + class_name: "Book" end ``` You can also set conditions via a hash: ```ruby -class Customer < ActiveRecord::Base - has_many :confirmed_orders, -> { where confirmed: true }, - class_name: "Order" +class Author < ApplicationRecord + has_many :confirmed_books, -> { where confirmed: true }, + class_name: "Book" end ``` -If you use a hash-style `where` option, then record creation via this association will be automatically scoped using the hash. In this case, using `@customer.confirmed_orders.create` or `@customer.confirmed_orders.build` will create orders where the confirmed column has the value `true`. +If you use a hash-style `where` option, then record creation via this association will be automatically scoped using the hash. In this case, using `@author.confirmed_books.create` or `@author.confirmed_books.build` will create books where the confirmed column has the value `true`. ##### `extending` @@ -1721,9 +1749,9 @@ The `extending` method specifies a named module to extend the association proxy. The `group` method supplies an attribute name to group the result set by, using a `GROUP BY` clause in the finder SQL. ```ruby -class Customer < ActiveRecord::Base - has_many :line_items, -> { group 'orders.id' }, - through: :orders +class Author < ApplicationRecord + has_many :line_items, -> { group 'books.id' }, + through: :books end ``` @@ -1732,34 +1760,34 @@ end You can use the `includes` method to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models: ```ruby -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author has_many :line_items end -class LineItem < ActiveRecord::Base - belongs_to :order +class LineItem < ApplicationRecord + belongs_to :book end ``` -If you frequently retrieve line items directly from customers (`@customer.orders.line_items`), then you can make your code somewhat more efficient by including line items in the association from customers to orders: +If you frequently retrieve line items directly from authors (`@author.books.line_items`), then you can make your code somewhat more efficient by including line items in the association from authors to books: ```ruby -class Customer < ActiveRecord::Base - has_many :orders, -> { includes :line_items } +class Author < ApplicationRecord + has_many :books, -> { includes :line_items } end -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author has_many :line_items end -class LineItem < ActiveRecord::Base - belongs_to :order +class LineItem < ApplicationRecord + belongs_to :book end ``` @@ -1768,10 +1796,10 @@ end The `limit` method lets you restrict the total number of objects that will be fetched through an association. ```ruby -class Customer < ActiveRecord::Base - has_many :recent_orders, - -> { order('order_date desc').limit(100) }, - class_name: "Order", +class Author < ApplicationRecord + has_many :recent_books, + -> { order('published_at desc').limit(100) }, + class_name: "Book", end ``` @@ -1784,8 +1812,8 @@ The `offset` method lets you specify the starting offset for fetching objects vi The `order` method dictates the order in which associated objects will be received (in the syntax used by an SQL `ORDER BY` clause). ```ruby -class Customer < ActiveRecord::Base - has_many :orders, -> { order "date_confirmed DESC" } +class Author < ApplicationRecord + has_many :books, -> { order "date_confirmed DESC" } end ``` @@ -1805,7 +1833,7 @@ Use the `distinct` method to keep the collection free of duplicates. This is mostly useful together with the `:through` option. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord has_many :readings has_many :articles, through: :readings end @@ -1844,11 +1872,21 @@ If you want to make sure that, upon insertion, all of the records in the persisted association are distinct (so that you can be sure that when you inspect the association that you will never find duplicate records), you should add a unique index on the table itself. For example, if you have a table named -`person_articles` and you want to make sure all the articles are unique, you could -add the following in a migration: +`readings` and you want to make sure the articles can only be added to a person once, +you could add the following in a migration: ```ruby -add_index :person_articles, :article, unique: true +add_index :readings, [:person_id, :article_id], unique: true +``` + +Once you have this unique index, attempting to add the article to a person twice +will raise an `ActiveRecord::RecordNotUnique` error: + +```ruby +person = Person.create(name: 'Honda') +article = Article.create(name: 'a1') +person.articles << article +person.articles << article # => ActiveRecord::RecordNotUnique ``` Note that checking for uniqueness using something like `include?` is subject @@ -1879,7 +1917,7 @@ The `has_and_belongs_to_many` association creates a many-to-many relationship wi When you declare a `has_and_belongs_to_many` association, the declaring class automatically gains 16 methods related to the association: -* `collection(force_reload = false)` +* `collection` * `collection<<(object, ...)` * `collection.delete(object, ...)` * `collection.destroy(object, ...)` @@ -1899,7 +1937,7 @@ When you declare a `has_and_belongs_to_many` association, the declaring class au In all of these methods, `collection` is replaced with the symbol passed as the first argument to `has_and_belongs_to_many`, and `collection_singular` is replaced with the singularized version of that symbol. For example, given the declaration: ```ruby -class Part < ActiveRecord::Base +class Part < ApplicationRecord has_and_belongs_to_many :assemblies end ``` @@ -1907,7 +1945,7 @@ end Each instance of the `Part` model will have these methods: ```ruby -assemblies(force_reload = false) +assemblies assemblies<<(object, ...) assemblies.delete(object, ...) assemblies.destroy(object, ...) @@ -1932,7 +1970,7 @@ If the join table for a `has_and_belongs_to_many` association has additional col WARNING: The use of extra attributes on the join table in a `has_and_belongs_to_many` association is deprecated. If you require this sort of complex behavior on the table that joins two models in a many-to-many relationship, you should use a `has_many :through` association instead of `has_and_belongs_to_many`. -##### `collection(force_reload = false)` +##### `collection` The `collection` method returns an array of all of the associated objects. If there are no associated objects, it returns an empty array. @@ -2053,7 +2091,7 @@ Does the same as `collection.create`, but raises `ActiveRecord::RecordInvalid` i While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `has_and_belongs_to_many` association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this association uses two such options: ```ruby -class Parts < ActiveRecord::Base +class Parts < ApplicationRecord has_and_belongs_to_many :assemblies, -> { readonly }, autosave: true end @@ -2075,7 +2113,7 @@ By convention, Rails assumes that the column in the join table used to hold the TIP: The `:foreign_key` and `:association_foreign_key` options are useful when setting up a many-to-many self-join. For example: ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord has_and_belongs_to_many :friends, class_name: "User", foreign_key: "this_user_id", @@ -2092,7 +2130,7 @@ If you set the `:autosave` option to `true`, Rails will save any loaded members If the name of the other model cannot be derived from the association name, you can use the `:class_name` option to supply the model name. For example, if a part has many assemblies, but the actual name of the model containing assemblies is `Gadget`, you'd set things up this way: ```ruby -class Parts < ActiveRecord::Base +class Parts < ApplicationRecord has_and_belongs_to_many :assemblies, class_name: "Gadget" end ``` @@ -2102,7 +2140,7 @@ end 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: ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord has_and_belongs_to_many :friends, class_name: "User", foreign_key: "this_user_id", @@ -2123,7 +2161,7 @@ If you set the `:validate` option to `false`, then associated objects will not b There may be times when you wish to customize the query used by `has_and_belongs_to_many`. Such customizations can be achieved via a scope block. For example: ```ruby -class Parts < ActiveRecord::Base +class Parts < ApplicationRecord has_and_belongs_to_many :assemblies, -> { where active: true } end ``` @@ -2139,14 +2177,14 @@ You can use any of the standard [querying methods](active_record_querying.html) * `order` * `readonly` * `select` -* `uniq` +* `distinct` ##### `where` The `where` method lets you specify the conditions that the associated object must meet. ```ruby -class Parts < ActiveRecord::Base +class Parts < ApplicationRecord has_and_belongs_to_many :assemblies, -> { where "factory = 'Seattle'" } end @@ -2155,7 +2193,7 @@ end You can also set conditions via a hash: ```ruby -class Parts < ActiveRecord::Base +class Parts < ApplicationRecord has_and_belongs_to_many :assemblies, -> { where factory: 'Seattle' } end @@ -2172,7 +2210,7 @@ The `extending` method specifies a named module to extend the association proxy. The `group` method supplies an attribute name to group the result set by, using a `GROUP BY` clause in the finder SQL. ```ruby -class Parts < ActiveRecord::Base +class Parts < ApplicationRecord has_and_belongs_to_many :assemblies, -> { group "factory" } end ``` @@ -2186,7 +2224,7 @@ You can use the `includes` method to specify second-order associations that shou The `limit` method lets you restrict the total number of objects that will be fetched through an association. ```ruby -class Parts < ActiveRecord::Base +class Parts < ApplicationRecord has_and_belongs_to_many :assemblies, -> { order("created_at DESC").limit(50) } end @@ -2201,7 +2239,7 @@ The `offset` method lets you specify the starting offset for fetching objects vi The `order` method dictates the order in which associated objects will be received (in the syntax used by an SQL `ORDER BY` clause). ```ruby -class Parts < ActiveRecord::Base +class Parts < ApplicationRecord has_and_belongs_to_many :assemblies, -> { order "assembly_name ASC" } end @@ -2215,9 +2253,9 @@ If you use the `readonly` method, then the associated objects will be read-only The `select` method lets you override the SQL `SELECT` clause that is used to retrieve data about the associated objects. By default, Rails retrieves all columns. -##### `uniq` +##### `distinct` -Use the `uniq` method to remove duplicates from the collection. +Use the `distinct` method to remove duplicates from the collection. #### When are Objects Saved? @@ -2243,10 +2281,10 @@ Association callbacks are similar to normal callbacks, but they are triggered by You define association callbacks by adding options to the association declaration. For example: ```ruby -class Customer < ActiveRecord::Base - has_many :orders, before_add: :check_credit_limit +class Author < ApplicationRecord + has_many :books, before_add: :check_credit_limit - def check_credit_limit(order) + def check_credit_limit(book) ... end end @@ -2257,15 +2295,15 @@ Rails passes the object being added or removed to the callback. You can stack callbacks on a single event by passing them as an array: ```ruby -class Customer < ActiveRecord::Base - has_many :orders, +class Author < ApplicationRecord + has_many :books, before_add: [:check_credit_limit, :calculate_shipping_charges] - def check_credit_limit(order) + def check_credit_limit(book) ... end - def calculate_shipping_charges(order) + def calculate_shipping_charges(book) ... end end @@ -2278,10 +2316,10 @@ If a `before_add` callback throws an exception, the object does not get added to You're not limited to the functionality that Rails automatically builds into association proxy objects. You can also extend these objects through anonymous modules, adding new finders, creators, or other methods. For example: ```ruby -class Customer < ActiveRecord::Base - has_many :orders do - def find_by_order_prefix(order_number) - find_by(region_id: order_number[0..2]) +class Author < ApplicationRecord + has_many :books do + def find_by_book_prefix(book_number) + find_by(category_id: book_number[0..2]) end end end @@ -2296,11 +2334,11 @@ module FindRecentExtension end end -class Customer < ActiveRecord::Base - has_many :orders, -> { extending FindRecentExtension } +class Author < ApplicationRecord + has_many :books, -> { extending FindRecentExtension } end -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_many :deliveries, -> { extending FindRecentExtension } end ``` diff --git a/guides/source/autoloading_and_reloading_constants.md b/guides/source/autoloading_and_reloading_constants.md index 2b6d7e4044..de0fa2fdc0 100644 --- a/guides/source/autoloading_and_reloading_constants.md +++ b/guides/source/autoloading_and_reloading_constants.md @@ -181,14 +181,14 @@ constant. That is, ```ruby -class Project < ActiveRecord::Base +class Project < ApplicationRecord end ``` performs a constant assignment equivalent to ```ruby -Project = Class.new(ActiveRecord::Base) +Project = Class.new(ApplicationRecord) ``` including setting the name of the class as a side-effect: @@ -685,7 +685,7 @@ to trigger the heuristic is defined in the conflicting place. ### Automatic Modules When a module acts as a namespace, Rails does not require the application to -defines a file for it, a directory matching the namespace is enough. +define a file for it, a directory matching the namespace is enough. Suppose an application has a back office whose controllers are stored in `app/controllers/admin`. If the `Admin` module is not yet loaded when @@ -790,7 +790,7 @@ Constant Reloading When `config.cache_classes` is false Rails is able to reload autoloaded constants. -For example, in you're in a console session and edit some file behind the +For example, if you're in a console session and edit some file behind the scenes, the code can be reloaded with the `reload!` command: ``` @@ -912,7 +912,7 @@ these classes: ```ruby # app/models/polygon.rb -class Polygon < ActiveRecord::Base +class Polygon < ApplicationRecord end # app/models/triangle.rb @@ -987,7 +987,7 @@ root class: ```ruby # app/models/polygon.rb -class Polygon < ActiveRecord::Base +class Polygon < ApplicationRecord end require_dependency ‘square’ ``` diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md index 20f11c2bc2..3a1a1ccfe6 100644 --- a/guides/source/caching_with_rails.md +++ b/guides/source/caching_with_rails.md @@ -3,12 +3,24 @@ Caching with Rails: An Overview =============================== -This guide is an introduction to speeding up your Rails app with caching. +This guide is an introduction to speeding up your Rails application with caching. + +Caching means to store content generated during the request-response cycle and +to reuse it when responding to similar requests. + +Caching is often the most effective way to boost an application's performance. +Through caching, web sites running on a single server with a single database +can sustain a load of thousands of concurrent users. + +Rails provides a set of caching features out of the box. This guide will teach +you the scope and purpose of each one of them. Master these techniques and your +Rails applications can serve millions of views without exorbitant response times +or server bills. After reading this guide, you will know: -* Page and action caching. * Fragment and Russian doll caching. +* How to manage the caching dependencies. * Alternative cache stores. * Conditional GET support. @@ -32,7 +44,7 @@ config.action_controller.perform_caching = true ``` NOTE: Changing the value of `config.action_controller.perform_caching` will -only have an effect on the caching provided by the Action Controller component. +only have an effect on the caching provided by the Action Controller component. For instance, it will not impact low-level caching, that we address [below](#low-level-caching). @@ -103,6 +115,30 @@ If you want to cache a fragment under certain conditions, you can use <% end %> ``` +#### Collection caching + +The `render` helper can also cache individual templates rendered for a collection. +It can even one up the previous example with `each` by reading all cache +templates at once instead of one by one. This is done automatically if the template +rendered by the collection includes a `cache` call. Take a collection that renders +a `products/_product.html.erb` partial for each element: + +```ruby +render products +``` + +If `products/_product.html.erb` starts with a `cache` call like so: + +```html+erb +<% cache product do %> + <%= product.name %> +<% end %> +``` + +All the cached templates from previous renders will be fetched at once with much +greater speed. There's more info on how to make your templates [eligible for +collection caching](http://api.rubyonrails.org/classes/ActionView/Template/Handlers/ERB.html#method-i-resource_cache_call_pattern). + ### Russian Doll Caching You may want to nest cached fragments inside other cached fragments. This is @@ -139,11 +175,11 @@ your app will serve stale data. To fix this, we tie the models together with the `touch` method: ```ruby -class Product < ActiveRecord::Base +class Product < ApplicationRecord has_many :games end -class Game < ActiveRecord::Base +class Game < ApplicationRecord belongs_to :product, touch: true end ``` @@ -152,6 +188,93 @@ With `touch` set to true, any action which changes `updated_at` for a game record will also change it for the associated product, thereby expiring the cache. +### Managing dependencies + +In order to correctly invalidate the cache, you need to properly define the +caching dependencies. Rails is clever enough to handle common cases so you don't +have to specify anything. However, sometimes, when you're dealing with custom +helpers for instance, you need to explicitly define them. + +#### Implicit dependencies + +Most template dependencies can be derived from calls to `render` in the template +itself. Here are some examples of render calls that `ActionView::Digestor` knows +how to decode: + +```ruby +render partial: "comments/comment", collection: commentable.comments +render "comments/comments" +render 'comments/comments' +render('comments/comments') + +render "header" => render("comments/header") + +render(@topic) => render("topics/topic") +render(topics) => render("topics/topic") +render(message.topics) => render("topics/topic") +``` + +On the other hand, some calls need to be changed to make caching work properly. +For instance, if you're passing a custom collection, you'll need to change: + +```ruby +render @project.documents.where(published: true) +``` + +to: + +```ruby +render partial: "documents/document", collection: @project.documents.where(published: true) +``` + +#### Explicit dependencies + +Sometimes you'll have template dependencies that can't be derived at all. This +is typically the case when rendering happens in helpers. Here's an example: + +```html+erb +<%= render_sortable_todolists @project.todolists %> +``` + +You'll need to use a special comment format to call those out: + +```html+erb +<%# Template Dependency: todolists/todolist %> +<%= render_sortable_todolists @project.todolists %> +``` + +In some cases, like a single table inheritance setup, you might have a bunch of +explicit dependencies. Instead of writing every template out, you can use a +wildcard to match any template in a directory: + +```html+erb +<%# Template Dependency: events/* %> +<%= render_categorizable_events @person.events %> +``` + +As for collection caching, if the partial template doesn't start with a clean +cache call, you can still benefit from collection caching by adding a special +comment format anywhere in the template, like: + +```html+erb +<%# Template Collection: notification %> +<% my_helper_that_calls_cache(some_arg, notification) do %> + <%= notification.name %> +<% end %> +``` + +#### External dependencies + +If you use a helper method, for example, inside a cached block and you then update +that helper, you'll have to bump the cache as well. It doesn't really matter how +you do it, but the md5 of the template file must change. One recommendation is to +simply be explicit in a comment, like: + +```html+erb +<%# Helper Dependency Updated: Jul 28, 2015 at 7pm %> +<%= some_helper_method(person) %> +``` + ### Low-Level Caching Sometimes you need to cache a particular value or query result instead of caching view fragments. Rails' caching mechanism works great for storing __any__ kind of information. @@ -161,7 +284,7 @@ The most efficient way to implement low-level caching is using the `Rails.cache. Consider the following example. An application has a `Product` model with an instance method that looks up the product’s price on a competing website. The data returned by this method would be perfect for low-level caching: ```ruby -class Product < ActiveRecord::Base +class Product < ApplicationRecord def competing_price Rails.cache.fetch("#{cache_key}/competing_price", expires_in: 12.hours) do Competitor::API.find_price(id) @@ -207,16 +330,17 @@ persistent fashion, you can with low level caching. Cache Stores ------------ -Rails provides different stores for the cached data created by **action** and **fragment** caches. - -TIP: Page caches are always stored on disk. +Rails provides different stores for the cached data (apart from SQL and page +caching). ### Configuration -You can set up your application's default cache store by calling `config.cache_store=` in the Application definition inside your `config/application.rb` file or in an Application.configure block in an environment specific configuration file (i.e. `config/environments/*.rb`). The first argument will be the cache store to use and the rest of the argument will be passed as arguments to the cache store constructor. +You can set up your application's default cache store by setting the +`config.cache_store` configuration option. Other parameters can be passed as +arguments to the cache store's constructor: ```ruby -config.cache_store = :memory_store +config.cache_store = :memory_store, { size: 64.megabytes } ``` NOTE: Alternatively, you can call `ActionController::Base.cache_store` outside of a configuration block. @@ -241,6 +365,19 @@ There are some common options used by all cache implementations. These can be pa * `:race_condition_ttl` - This option is used in conjunction with the `:expires_in` option. It will prevent race conditions when cache entries expire by preventing multiple processes from simultaneously regenerating the same entry (also known as the dog pile effect). This option sets the number of seconds that an expired entry can be reused while a new value is being regenerated. It's a good practice to set this value if you use the `:expires_in` option. +#### Custom Cache Stores + +You can create your own custom cache store by simply extending +`ActiveSupport::Cache::Store` and implementing the appropriate methods. This way, +you can swap in any number of caching technologies into your Rails application. + +To use a custom cache store, simply set the cache store to a new instance of your +custom class. + +```ruby +config.cache_store = MyCacheStore.new +``` + ### ActiveSupport::Cache::MemoryStore This cache store keeps entries in memory in the same Ruby process. The cache @@ -292,36 +429,6 @@ The `write` and `fetch` methods on this cache accept two additional options that config.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.com" ``` -### ActiveSupport::Cache::EhcacheStore - -If you are using JRuby you can use Terracotta's Ehcache as the cache store for your application. Ehcache is an open source Java cache that also offers an enterprise version with increased scalability, management, and commercial support. You must first install the jruby-ehcache-rails3 gem (version 1.1.0 or later) to use this cache store. - -```ruby -config.cache_store = :ehcache_store -``` - -When initializing the cache, you may use the `:ehcache_config` option to specify the Ehcache config file to use (where the default is "ehcache.xml" in your Rails config directory), and the :cache_name option to provide a custom name for your cache (the default is rails_cache). - -In addition to the standard `:expires_in` option, the `write` method on this cache can also accept the additional `:unless_exist` option, which will cause the cache store to use Ehcache's `putIfAbsent` method instead of `put`, and therefore will not overwrite an existing entry. Additionally, the `write` method supports all of the properties exposed by the [Ehcache Element class](http://ehcache.org/apidocs/net/sf/ehcache/Element.html) , including: - -| Property | Argument Type | Description | -| --------------------------- | ------------------- | ----------------------------------------------------------- | -| elementEvictionData | ElementEvictionData | Sets this element's eviction data instance. | -| eternal | boolean | Sets whether the element is eternal. | -| timeToIdle, tti | int | Sets time to idle | -| timeToLive, ttl, expires_in | int | Sets time to Live | -| version | long | Sets the version attribute of the ElementAttributes object. | - -These options are passed to the `write` method as Hash options using either camelCase or underscore notation, as in the following examples: - -```ruby -Rails.cache.write('key', 'value', time_to_idle: 60.seconds, timeToLive: 600.seconds) -caches_action :index, expires_in: 60.seconds, unless_exist: true -``` - -For more information about Ehcache, see [http://ehcache.org/](http://ehcache.org/) . -For more information about Ehcache for JRuby and Rails, see [http://ehcache.org/documentation/jruby.html](http://ehcache.org/documentation/jruby.html) - ### ActiveSupport::Cache::NullStore This cache store implementation is meant to be used only in development or test environments and it never stores anything. This can be very useful in development when you have code that interacts directly with `Rails.cache` but caching may interfere with being able to see the results of code changes. With this cache store, all `fetch` and `read` operations will result in a miss. @@ -330,19 +437,13 @@ This cache store implementation is meant to be used only in development or test config.cache_store = :null_store ``` -### Custom Cache Stores - -You can create your own custom cache store by simply extending `ActiveSupport::Cache::Store` and implementing the appropriate methods. In this way, you can swap in any number of caching technologies into your Rails application. - -To use a custom cache store, simply set the cache store to a new instance of the class. - -```ruby -config.cache_store = MyCacheStore.new -``` - -### Cache Keys +Cache Keys +---------- -The keys used in a cache can be any object that responds to either `:cache_key` or `:to_param`. You can implement the `:cache_key` method on your classes if you need to generate custom keys. Active Record will generate keys based on the class name and record id. +The keys used in a cache can be any object that responds to either `cache_key` or +`to_param`. You can implement the `cache_key` method on your classes if you need +to generate custom keys. Active Record will generate keys based on the class name +and record id. You can use Hashes and Arrays of values as cache keys. @@ -351,7 +452,12 @@ You can use Hashes and Arrays of values as cache keys. Rails.cache.read(site: "mysite", owners: [owner_1, owner_2]) ``` -The keys you use on `Rails.cache` will not be the same as those actually used with the storage engine. They may be modified with a namespace or altered to fit technology backend constraints. This means, for instance, that you can't save values with `Rails.cache` and then try to pull them out with the `memcache-client` gem. However, you also don't need to worry about exceeding the memcached size limit or violating syntax rules. +The keys you use on `Rails.cache` will not be the same as those actually used with +the storage engine. They may be modified with a namespace or altered to fit +technology backend constraints. This means, for instance, that you can't save +values with `Rails.cache` and then try to pull them out with the `dalli` gem. +However, you also don't need to worry about exceeding the memcached size limit or +violating syntax rules. Conditional GET support ----------------------- diff --git a/guides/source/command_line.md b/guides/source/command_line.md index 0f5a9e4e39..e25992fdef 100644 --- a/guides/source/command_line.md +++ b/guides/source/command_line.md @@ -21,7 +21,7 @@ There are a few commands that are absolutely critical to your everyday usage of * `rails console` * `rails server` -* `rake` +* `bin/rails` * `rails generate` * `rails dbconsole` * `rails new app_name` @@ -39,7 +39,7 @@ INFO: You can install the rails gem by typing `gem install rails`, if you don't ```bash $ rails new commandsapp create - create README.rdoc + create README.md create Rakefile create config.ru create .gitignore @@ -55,20 +55,21 @@ Rails will set you up with what seems like a huge amount of stuff for such a tin ### `rails server` -The `rails server` command launches a small web server named WEBrick which comes bundled with Ruby. You'll use this any time you want to access your application through a web browser. +The `rails server` command launches a web server named Puma which comes bundled with Rails. You'll use this any time you want to access your application through a web browser. With no further work, `rails server` will run our new shiny Rails app: ```bash $ cd commandsapp $ bin/rails server -=> Booting WEBrick -=> Rails 5.0.0 application starting in development on http://localhost:3000 +=> Booting Puma +=> Rails 5.0.0 application starting in development on http://0.0.0.0:3000 => Run `rails server -h` for more startup options => Ctrl-C to shutdown server -[2013-08-07 02:00:01] INFO WEBrick 1.3.1 -[2013-08-07 02:00:01] INFO ruby 2.2.2 (2015-06-27) [x86_64-darwin11.2.0] -[2013-08-07 02:00:01] INFO WEBrick::HTTPServer#start: pid=69680 port=3000 +Puma 2.15.3 starting... +* Min threads: 0, max threads: 16 +* Environment: development +* Listening on tcp://localhost:3000 ``` With just three commands we whipped up a Rails server listening on port 3000. Go to your browser and open [http://localhost:3000](http://localhost:3000), you will see a basic Rails app running. @@ -181,7 +182,7 @@ Fire up your server using `rails server`. ```bash $ bin/rails server -=> Booting WEBrick... +=> Booting Puma... ``` The URL will be [http://localhost:3000/greetings/hello](http://localhost:3000/greetings/hello). @@ -250,17 +251,23 @@ $ bin/rails generate scaffold HighScore game:string score:integer The generator checks that there exist the directories for models, controllers, helpers, layouts, functional and unit tests, stylesheets, creates the views, controller, model and database migration for HighScore (creating the `high_scores` table and fields), takes care of the route for the **resource**, and new tests for everything. -The migration requires that we **migrate**, that is, run some Ruby code (living in that `20130717151933_create_high_scores.rb`) to modify the schema of our database. Which database? The SQLite3 database that Rails will create for you when we run the `rake db:migrate` command. We'll talk more about Rake in-depth in a little while. +The migration requires that we **migrate**, that is, run some Ruby code (living in that `20130717151933_create_high_scores.rb`) to modify the schema of our database. Which database? The SQLite3 database that Rails will create for you when we run the `bin/rails db:migrate` command. We'll talk more about bin/rails in-depth in a little while. ```bash -$ bin/rake db:migrate +$ bin/rails db:migrate == CreateHighScores: migrating =============================================== -- create_table(:high_scores) -> 0.0017s == CreateHighScores: migrated (0.0019s) ====================================== ``` -INFO: Let's talk about unit tests. Unit tests are code that tests and makes assertions about code. In unit testing, we take a little part of code, say a method of a model, and test its inputs and outputs. Unit tests are your friend. The sooner you make peace with the fact that your quality of life will drastically increase when you unit test your code, the better. Seriously. We'll make one in a moment. +INFO: Let's talk about unit tests. Unit tests are code that tests and makes assertions +about code. In unit testing, we take a little part of code, say a method of a model, +and test its inputs and outputs. Unit tests are your friend. The sooner you make +peace with the fact that your quality of life will drastically increase when you unit +test your code, the better. Seriously. Please visit +[the testing guide](http://guides.rubyonrails.org/testing.html) for an in-depth +look at unit testing. Let's see the interface Rails created for us. @@ -369,44 +376,63 @@ $ bin/rails destroy model Oops remove test/fixtures/oops.yml ``` -Rake ----- +bin/rails +--------- -Rake is Ruby Make, a standalone Ruby utility that replaces the Unix utility 'make', and uses a 'Rakefile' and `.rake` files to build up a list of tasks. In Rails, Rake is used for common administration tasks, especially sophisticated ones that build off of each other. +Since Rails 5.0+ has rake commands built into the rails executable, `bin/rails` is the new default for running commands. -You can get a list of Rake tasks available to you, which will often depend on your current directory, by typing `rake --tasks`. Each task has a description, and should help you find the thing you need. - -To get the full backtrace for running rake task you can pass the option `--trace` to command line, for example `rake db:create --trace`. +You can get a list of bin/rails tasks available to you, which will often depend on your current directory, by typing `bin/rails --help`. Each task has a description, and should help you find the thing you need. ```bash -$ bin/rake --tasks -rake about # List versions of all Rails frameworks and the environment -rake assets:clean # Remove old compiled assets -rake assets:clobber # Remove compiled assets -rake assets:precompile # Compile all the assets named in config.assets.precompile -rake db:create # Create the database from config/database.yml for the current Rails.env +$ bin/rails --help +Usage: rails COMMAND [ARGS] + +The most common rails commands are: +generate Generate new code (short-cut alias: "g") +console Start the Rails console (short-cut alias: "c") +server Start the Rails server (short-cut alias: "s") +... + +All commands can be run with -h (or --help) for more information. + +In addition to those commands, there are: +about List versions of all Rails ... +assets:clean[keep] Remove old compiled assets +assets:clobber Remove compiled assets +assets:environment Load asset compile environment +assets:precompile Compile all the assets ... ... -rake log:clear # Truncates all *.log files in log/ to zero bytes (specify which logs with LOGS=test,development) -rake middleware # Prints out your Rack middleware stack +db:fixtures:load Loads fixtures into the ... +db:migrate Migrate the database ... +db:migrate:status Display status of migrations +db:rollback Rolls the schema back to ... +db:schema:cache:clear Clears a db/schema_cache.dump file +db:schema:cache:dump Creates a db/schema_cache.dump file +db:schema:dump Creates a db/schema.rb file ... +db:schema:load Loads a schema.rb file ... +db:seed Loads the seed data ... +db:structure:dump Dumps the database structure ... +db:structure:load Recreates the databases ... +db:version Retrieves the current schema ... ... -rake tmp:clear # Clear cache and socket files from tmp/ (narrow w/ tmp:cache:clear, tmp:sockets:clear) -rake tmp:create # Creates tmp directories for cache, sockets, and pids +restart Restart app by touching ... +tmp:create Creates tmp directories ... ``` -INFO: You can also use `rake -T` to get the list of tasks. +INFO: You can also use `bin/rails -T` to get the list of tasks. ### `about` -`rake about` gives information about version numbers for Ruby, RubyGems, Rails, the Rails subcomponents, your application's folder, the current Rails environment name, your app's database adapter, and schema version. It is useful when you need to ask for help, check if a security patch might affect you, or when you need some stats for an existing Rails installation. +`bin/rails about` gives information about version numbers for Ruby, RubyGems, Rails, the Rails subcomponents, your application's folder, the current Rails environment name, your app's database adapter, and schema version. It is useful when you need to ask for help, check if a security patch might affect you, or when you need some stats for an existing Rails installation. ```bash -$ bin/rake about +$ bin/rails about About your application's environment Rails version 5.0.0 Ruby version 2.2.2 (x86_64-linux) RubyGems version 2.4.6 Rack version 1.6 JavaScript Runtime Node.js (V8) -Middleware Rack::Sendfile, ActionDispatch::Static, Rack::Lock, #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007ffd131a7c88>, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, Rails::Rack::Logger, ActionDispatch::ShowExceptions, ActionDispatch::DebugExceptions, ActionDispatch::RemoteIp, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::Migration::CheckPending, ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, ActionDispatch::ParamsParser, Rack::Head, Rack::ConditionalGet, Rack::ETag +Middleware Rack::Sendfile, ActionDispatch::Static, ActionDispatch::LoadInterlock, #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007ffd131a7c88>, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, Rails::Rack::Logger, ActionDispatch::ShowExceptions, ActionDispatch::DebugExceptions, ActionDispatch::RemoteIp, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::Migration::CheckPending, ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, Rack::Head, Rack::ConditionalGet, Rack::ETag Application root /home/foobar/commandsapp Environment development Database adapter sqlite3 @@ -415,22 +441,22 @@ Database schema version 20110805173523 ### `assets` -You can precompile the assets in `app/assets` using `rake assets:precompile`, and remove older compiled assets using `rake assets:clean`. The `assets:clean` task allows for rolling deploys that may still be linking to an old asset while the new assets are being built. +You can precompile the assets in `app/assets` using `bin/rails assets:precompile`, and remove older compiled assets using `bin/rails assets:clean`. The `assets:clean` task allows for rolling deploys that may still be linking to an old asset while the new assets are being built. -If you want to clear `public/assets` completely, you can use `rake assets:clobber`. +If you want to clear `public/assets` completely, you can use `bin/rails assets:clobber`. ### `db` -The most common tasks of the `db:` Rake namespace are `migrate` and `create`, and it will pay off to try out all of the migration rake tasks (`up`, `down`, `redo`, `reset`). `rake db:version` is useful when troubleshooting, telling you the current version of the database. +The most common tasks of the `db:` bin/rails namespace are `migrate` and `create`, and it will pay off to try out all of the migration bin/rails tasks (`up`, `down`, `redo`, `reset`). `bin/rails db:version` is useful when troubleshooting, telling you the current version of the database. More information about migrations can be found in the [Migrations](active_record_migrations.html) guide. ### `notes` -`rake notes` will search through your code for comments beginning with FIXME, OPTIMIZE or TODO. The search is done in files with extension `.builder`, `.rb`, `.rake`, `.yml`, `.yaml`, `.ruby`, `.css`, `.js` and `.erb` for both default and custom annotations. +`bin/rails notes` will search through your code for comments beginning with FIXME, OPTIMIZE or TODO. The search is done in files with extension `.builder`, `.rb`, `.rake`, `.yml`, `.yaml`, `.ruby`, `.css`, `.js` and `.erb` for both default and custom annotations. ```bash -$ bin/rake notes +$ bin/rails notes (in /home/foobar/commandsapp) app/controllers/admin/users_controller.rb: * [ 20] [TODO] any other way to do this? @@ -447,10 +473,10 @@ You can add support for new file extensions using `config.annotations.register_e config.annotations.register_extensions("scss", "sass", "less") { |annotation| /\/\/\s*(#{annotation}):?\s*(.*)$/ } ``` -If you are looking for a specific annotation, say FIXME, you can use `rake notes:fixme`. Note that you have to lower case the annotation's name. +If you are looking for a specific annotation, say FIXME, you can use `bin/rails notes:fixme`. Note that you have to lower case the annotation's name. ```bash -$ bin/rake notes:fixme +$ bin/rails notes:fixme (in /home/foobar/commandsapp) app/controllers/admin/users_controller.rb: * [132] high priority for next deploy @@ -459,10 +485,10 @@ app/models/school.rb: * [ 17] ``` -You can also use custom annotations in your code and list them using `rake notes:custom` by specifying the annotation using an environment variable `ANNOTATION`. +You can also use custom annotations in your code and list them using `bin/rails notes:custom` by specifying the annotation using an environment variable `ANNOTATION`. ```bash -$ bin/rake notes:custom ANNOTATION=BUG +$ bin/rails notes:custom ANNOTATION=BUG (in /home/foobar/commandsapp) app/models/article.rb: * [ 23] Have to fix this one before pushing! @@ -470,11 +496,11 @@ app/models/article.rb: NOTE. When using specific annotations and custom annotations, the annotation name (FIXME, BUG etc) is not displayed in the output lines. -By default, `rake notes` will look in the `app`, `config`, `db`, `lib` and `test` directories. If you would like to search other directories, you can provide them as a comma separated list in an environment variable `SOURCE_ANNOTATION_DIRECTORIES`. +By default, `rails notes` will look in the `app`, `config`, `db`, `lib` and `test` directories. If you would like to search other directories, you can provide them as a comma separated list in an environment variable `SOURCE_ANNOTATION_DIRECTORIES`. ```bash $ export SOURCE_ANNOTATION_DIRECTORIES='spec,vendor' -$ bin/rake notes +$ bin/rails notes (in /home/foobar/commandsapp) app/models/user.rb: * [ 35] [FIXME] User should have a subscription at this point @@ -484,7 +510,7 @@ spec/models/user_spec.rb: ### `routes` -`rake routes` will list all of your defined routes, which is useful for tracking down routing problems in your app, or giving you a good overview of the URLs in an app you're trying to get familiar with. +`rails routes` will list all of your defined routes, which is useful for tracking down routing problems in your app, or giving you a good overview of the URLs in an app you're trying to get familiar with. ### `test` @@ -498,16 +524,16 @@ The `Rails.root/tmp` directory is, like the *nix /tmp directory, the holding pla The `tmp:` namespaced tasks will help you clear and create the `Rails.root/tmp` directory: -* `rake tmp:cache:clear` clears `tmp/cache`. -* `rake tmp:sockets:clear` clears `tmp/sockets`. -* `rake tmp:clear` clears all cache and sockets files. -* `rake tmp:create` creates tmp directories for cache, sockets and pids. +* `rails tmp:cache:clear` clears `tmp/cache`. +* `rails tmp:sockets:clear` clears `tmp/sockets`. +* `rails tmp:clear` clears all cache and sockets files. +* `rails tmp:create` creates tmp directories for cache, sockets and pids. ### Miscellaneous -* `rake stats` is great for looking at statistics on your code, displaying things like KLOCs (thousands of lines of code) and your code to test ratio. -* `rake secret` will give you a pseudo-random key to use for your session secret. -* `rake time:zones:all` lists all the timezones Rails knows about. +* `rails stats` is great for looking at statistics on your code, displaying things like KLOCs (thousands of lines of code) and your code to test ratio. +* `rails secret` will give you a pseudo-random key to use for your session secret. +* `rails time:zones:all` lists all the timezones Rails knows about. ### Custom Rake Tasks @@ -545,9 +571,9 @@ end Invocation of the tasks will look like: ```bash -$ bin/rake task_name -$ bin/rake "task_name[value 1]" # entire argument string should be quoted -$ bin/rake db:nothing +$ bin/rails task_name +$ bin/rails "task_name[value 1]" # entire argument string should be quoted +$ bin/rails db:nothing ``` NOTE: If your need to interact with your application models, perform database queries and so on, your task should depend on the `environment` task, which will load your application code. @@ -578,8 +604,8 @@ $ rails new . --git --database=postgresql create tmp/pids create Rakefile add 'Rakefile' - create README.rdoc -add 'README.rdoc' + create README.md +add 'README.md' create app/controllers/application_controller.rb add 'app/controllers/application_controller.rb' create app/helpers/application_helper.rb diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 9c80e73c73..d9c345fb71 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -35,7 +35,7 @@ In general, the work of configuring Rails means configuring the components of Ra For example, the `config/application.rb` file includes this setting: ```ruby -config.autoload_paths += %W(#{config.root}/extras) +config.time_zone = 'Central Time (US & Canada)' ``` This is a setting for Rails itself. If you want to pass settings to individual Rails components, you can do so via the same `config` object in `config/application.rb`: @@ -98,13 +98,13 @@ application. Accepts a valid week day symbol (e.g. `:monday`). * `config.exceptions_app` sets the exceptions application invoked by the ShowException middleware when an exception happens. Defaults to `ActionDispatch::PublicExceptions.new(Rails.public_path)`. -* `config.file_watcher` the class used to detect file updates in the filesystem when `config.reload_classes_only_on_change` is true. Must conform to `ActiveSupport::FileUpdateChecker` API. +* `config.file_watcher` is the class used to detect file updates in the file system when `config.reload_classes_only_on_change` is true. Rails ships with `ActiveSupport::FileUpdateChecker`, the default, and `ActiveSupport::EventedFileUpdateChecker` (this one depends on the [listen](https://github.com/guard/listen) gem). Custom classes must conform to the `ActiveSupport::FileUpdateChecker` API. * `config.filter_parameters` used for filtering out the parameters that you don't want shown in the logs, such as passwords or credit card numbers. New applications filter out passwords by adding the following `config.filter_parameters+=[:password]` in `config/initializers/filter_parameter_logging.rb`. -* `config.force_ssl` forces all requests to be under HTTPS protocol by using `ActionDispatch::SSL` middleware. +* `config.force_ssl` forces all requests to be served over HTTPS by using the `ActionDispatch::SSL` middleware. This can be configured by setting `config.ssl_options` - see the [ActionDispatch::SSL documentation](http://edgeapi.rubyonrails.org/classes/ActionDispatch/SSL.html) for details. * `config.log_formatter` defines the formatter of the Rails logger. This option defaults to an instance of `ActiveSupport::Logger::SimpleFormatter` for all modes except production, where it defaults to `Logger::Formatter`. @@ -122,7 +122,7 @@ defaults to `:debug` for all environments. The available log levels are: `:debug * `secrets.secret_key_base` is used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get `secrets.secret_key_base` initialized to a random key present in `config/secrets.yml`. -* `config.serve_static_files` configures Rails to serve static files. This option defaults to true, but in the production environment it is set to false because the server software (e.g. NGINX or Apache) used to run the application should serve static files instead. If you are running or testing your app in production mode using WEBrick (it is not recommended to use WEBrick in production) set the option to true. Otherwise, you won't be able to use page caching and request for files that exist under the public directory. +* `config.public_file_server.enabled` configures Rails to serve static files from the public directory. This option defaults to true, but in the production environment it is set to false because the server software (e.g. NGINX or Apache) used to run the application should serve static files instead. If you are running or testing your app in production mode using WEBrick (it is not recommended to use WEBrick in production) set the option to true. Otherwise, you won't be able to use page caching and request for files that exist under the public directory. * `config.session_store` is usually set up in `config/initializers/session_store.rb` and specifies what class to use to store the session. Possible values are `:cookie_store` which is the default, `:mem_cache_store`, and `:disabled`. The last one tells Rails not to deal with sessions. Custom session stores can also be specified: @@ -159,8 +159,6 @@ pipeline is enabled. It is set to true by default. * `config.assets.debug` disables the concatenation and compression of assets. Set to `true` by default in `development.rb`. -* `config.assets.cache_store` defines the cache store that Sprockets will use. The default is the Rails file store. - * `config.assets.compile` is a boolean that can be used to turn on live Sprockets compilation in production. * `config.assets.logger` accepts a logger conforming to the interface of Log4r or the default Ruby `Logger` class. Defaults to the same configured at `config.logger`. Setting `config.assets.logger` to false will turn off served assets logging. @@ -198,9 +196,9 @@ The full set of methods that can be used in this block are as follows: Every Rails application comes with a standard set of middleware which it uses in this order in the development environment: -* `ActionDispatch::SSL` forces every request to be under HTTPS protocol. Will be available if `config.force_ssl` is set to `true`. Options passed to this can be configured by using `config.ssl_options`. -* `ActionDispatch::Static` is used to serve static assets. Disabled if `config.serve_static_files` is `false`. Set `config.static_index` if you need to serve a static directory index file that is not named `index`. For example, to serve `main.html` instead of `index.html` for directory requests, set `config.static_index` to `"main"`. -* `Rack::Lock` wraps the app in mutex so it can only be called by a single thread at a time. Only enabled when `config.cache_classes` is `false`. +* `ActionDispatch::SSL` forces every request to be served using HTTPS. Enabled if `config.force_ssl` is set to `true`. Options passed to this can be configured by setting `config.ssl_options`. +* `ActionDispatch::Static` is used to serve static assets. Disabled if `config.public_file_server.enabled` is `false`. Set `config.public_file_server.index_name` if you need to serve a static directory index file that is not named `index`. For example, to serve `main.html` instead of `index.html` for directory requests, set `config.public_file_server.index_name` to `"main"`. +* `ActionDispatch::LoadInterlock` allows thread safe code reloading. Disabled if `config.allow_concurrency` is `false`, which causes `Rack::Lock` to be loaded. `Rack::Lock` wraps the app in mutex so it can only be called by a single thread at a time. * `ActiveSupport::Cache::Strategy::LocalCache` serves as a basic memory backed cache. This cache is not thread safe and is intended only for serving as a temporary memory cache for a single thread. * `Rack::Runtime` sets an `X-Runtime` header, containing the time (in seconds) taken to execute the request. * `Rails::Rack::Logger` notifies the logs that the request has begun. After request is complete, flushes all the logs. @@ -214,7 +212,6 @@ Every Rails application comes with a standard set of middleware which it uses in * `ActionDispatch::Cookies` sets cookies for the request. * `ActionDispatch::Session::CookieStore` is responsible for storing the session in cookies. An alternate middleware can be used for this by changing the `config.action_controller.session_store` to an alternate value. Additionally, options passed to this can be configured by using `config.action_controller.session_options`. * `ActionDispatch::Flash` sets up the `flash` keys. Only available if `config.action_controller.session_store` is set to a value. -* `ActionDispatch::ParamsParser` parses out parameters from the request into `params`. * `Rack::MethodOverride` allows the method to be overridden if `params[:_method]` is set. This is the middleware which supports the PATCH, PUT, and DELETE HTTP method types. * `Rack::Head` converts HEAD requests to GET requests and serves them as so. @@ -245,7 +242,7 @@ config.middleware.swap ActionController::Failsafe, Lifo::Failsafe They can also be removed from the stack completely: ```ruby -config.middleware.delete "Rack::MethodOverride" +config.middleware.delete Rack::MethodOverride ``` ### Configuring i18n @@ -267,8 +264,8 @@ All these configuration options are delegated to the `I18n` library. * `config.active_record.logger` accepts a logger conforming to the interface of Log4r or the default Ruby Logger class, which is then passed on to any new database connections made. You can retrieve this logger by calling `logger` on either an Active Record model class or an Active Record model instance. Set to `nil` to disable logging. * `config.active_record.primary_key_prefix_type` lets you adjust the naming for primary key columns. By default, Rails assumes that primary key columns are named `id` (and this configuration option doesn't need to be set.) There are two other choices: -** `:table_name` would make the primary key for the Customer class `customerid` -** `:table_name_with_underscore` would make the primary key for the Customer class `customer_id` + * `:table_name` would make the primary key for the Customer class `customerid` + * `:table_name_with_underscore` would make the primary key for the Customer class `customer_id` * `config.active_record.table_name_prefix` lets you set a global string to be prepended to table names. If you set this to `northwest_`, then the Customer class will look for `northwest_customers` as its table. The default is an empty string. @@ -305,16 +302,22 @@ All these configuration options are delegated to the `I18n` library. `:all` which always dumps all schemas regardless of the schema_search_path, or a string of comma separated schemas. -* `config.active_record.belongs_to_required_by_default` is a boolean value and controls whether `belongs_to` association is required by default. +* `config.active_record.belongs_to_required_by_default` is a boolean value and + controls whether a record fails validation if `belongs_to` association is not + present. * `config.active_record.warn_on_records_fetched_greater_than` allows setting a warning threshold for query result size. If the number of records returned by a query exceeds the threshold, a warning is logged. This can be used to identify queries which might be causing memory bloat. +* `config.active_record.index_nested_attribute_errors` allows errors for nested + has_many relationships to be displayed with an index as well as the error. + Defaults to false. + The MySQL adapter adds one additional configuration option: -* `ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans` controls whether Active Record will consider all `tinyint(1)` columns in a MySQL database to be booleans and is true by default. +* `ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_booleans` controls whether Active Record will consider all `tinyint(1)` columns in a MySQL database to be booleans and is true by default. The schema dumper adds one additional configuration option: @@ -330,8 +333,6 @@ The schema dumper adds one additional configuration option: * `config.action_controller.default_static_extension` configures the extension used for cached pages. Defaults to `.html`. -* `config.action_controller.default_charset` specifies the default character set for all renders. The default is "utf-8". - * `config.action_controller.include_all_helpers` configures whether all view helpers are available everywhere or are scoped to the corresponding controller. If set to `false`, `UsersHelper` methods are only available for views rendered as part of `UsersController`. If `true`, `UsersHelper` methods are available everywhere. The default is `true`. * `config.action_controller.logger` accepts a logger conforming to the interface of Log4r or the default Ruby Logger class, which is then used to log information from Action Controller. Set to `nil` to disable logging. @@ -340,6 +341,10 @@ The schema dumper adds one additional configuration option: * `config.action_controller.allow_forgery_protection` enables or disables CSRF protection. By default this is `false` in test mode and `true` in all other modes. +* `config.action_controller.forgery_protection_origin_check` configures whether the HTTP `Origin` header should be checked against the site's origin as an additional CSRF defense. + +* `config.action_controller.per_form_csrf_tokens` configures whether CSRF tokens are only valid for the method/action they were generated for. + * `config.action_controller.relative_url_root` can be used to tell Rails that you are [deploying to a subdirectory](configuring.html#deploy-to-a-subdirectory-relative-url-root). The default is `ENV['RAILS_RELATIVE_URL_ROOT']`. * `config.action_controller.permit_all_parameters` sets all the parameters for mass assignment to be permitted by default. The default value is `false`. @@ -362,6 +367,8 @@ The schema dumper adds one additional configuration option: } ``` +* `config.action_dispatch.default_charset` specifies the default character set for all renders. Defaults to `nil`. + * `config.action_dispatch.tld_length` sets the TLD (top-level domain) length for the application. Defaults to `1`. * `config.action_dispatch.http_auth_salt` sets the HTTP Auth salt value. Defaults @@ -414,7 +421,7 @@ encrypted cookies salt value. Defaults to `'signed encrypted cookie'`. `config.action_view` includes a small number of configuration settings: -* `config.action_view.field_error_proc` provides an HTML generator for displaying errors that come from Active Record. The default is +* `config.action_view.field_error_proc` provides an HTML generator for displaying errors that come from Active Model. The default is ```ruby Proc.new do |html_tag, instance| @@ -451,6 +458,11 @@ encrypted cookies salt value. Defaults to `'signed encrypted cookie'`. * `config.action_view.raise_on_missing_translations` determines whether an error should be raised for missing translations. +* `config.action_view.automatically_disable_submit_tag` determines whether + submit_tag should automatically disable on click, this defaults to true. + +* `config.action_view.debug_missing_translation` determins whether to wrap the missing translations key in a `<span>` tag or not. This defaults to true. + ### Configuring Action Mailer There are a number of settings available on `config.action_mailer`: @@ -471,7 +483,7 @@ There are a number of settings available on `config.action_mailer`: * `config.action_mailer.raise_delivery_errors` specifies whether to raise an error if email delivery cannot be completed. It defaults to true. -* `config.action_mailer.delivery_method` defines the delivery method and defaults to `:smtp`. See the [configuration section in the Action Mailer guide](http://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration) for more info. +* `config.action_mailer.delivery_method` defines the delivery method and defaults to `:smtp`. See the [configuration section in the Action Mailer guide](action_mailer_basics.html#action-mailer-configuration) for more info. * `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. @@ -533,7 +545,7 @@ There are a few configuration options available in Active Support: * `config.active_support.time_precision` sets the precision of JSON encoded time values. Defaults to `3`. -* `config.active_support.halt_callback_chains_on_return_false` specifies whether ActiveRecord, ActiveModel and ActiveModel::Validations callback chains can be halted by returning `false` in a 'before' callback. Defaults to `true`. +* `ActiveSupport.halt_callback_chains_on_return_false` specifies whether Active Record and Active Model callback chains can be halted by returning `false` in a 'before' callback. When set to `false`, callback chains are halted only when explicitly done so with `throw(:abort)`. When set to `true`, callback chains are halted when a callback returns false (the previous behavior before Rails 5) and a deprecation warning is given. Defaults to `true` during the deprecation period. New Rails 5 apps generate an initializer file called `callback_terminator.rb` which sets the value to `false`. This file is *not* added when running `rake rails:update`, so returning `false` will still work on older apps ported to Rails 5 and display a deprecation warning to prompt users to update their code. * `ActiveSupport::Logger.silencer` is set to `false` to disable the ability to silence logging in a block. The default is `true`. @@ -639,7 +651,7 @@ TIP: You don't have to update the database configurations manually. If you look ### Connection Preference -Since there are two ways to set your connection, via environment variable it is important to understand how the two can interact. +Since there are two ways to configure your connection (using `config/database.yml` or using an environment variable) it is important to understand how they can interact. If you have an empty `config/database.yml` file but your `ENV['DATABASE_URL']` is present, then Rails will connect to the database via your environment variable: @@ -1035,9 +1047,9 @@ Below is a comprehensive list of all the initializers found in Rails in the orde * `action_mailer.compile_config_methods` Initializes methods for the config settings specified so that they are quicker to access. -* `set_load_path` This initializer runs before `bootstrap_hook`. Adds the `vendor`, `lib`, all directories of `app` and any paths specified by `config.load_paths` to `$LOAD_PATH`. +* `set_load_path` This initializer runs before `bootstrap_hook`. Adds paths specified by `config.load_paths` and all autoload paths to `$LOAD_PATH`. -* `set_autoload_paths` This initializer runs before `bootstrap_hook`. Adds all sub-directories of `app` and paths specified by `config.autoload_paths` to `ActiveSupport::Dependencies.autoload_paths`. +* `set_autoload_paths` This initializer runs before `bootstrap_hook`. Adds all sub-directories of `app` and paths specified by `config.autoload_paths`, `config.eager_load_paths` and `config.autoload_once_paths` to `ActiveSupport::Dependencies.autoload_paths`. * `add_routing_paths` Loads (by default) all `config/routes.rb` files (in the application and railties, including engines) and sets up the routes for the application. @@ -1093,7 +1105,7 @@ you and wait for a connection from the pool. If it cannot get a connection, a timeout error similar to that given below will be thrown. ```ruby -ActiveRecord::ConnectionTimeoutError - could not obtain a database connection within 5 seconds. The max pool size is currently 5; consider increasing it: +ActiveRecord::ConnectionTimeoutError - could not obtain a database connection within 5.000 seconds (waited 5.000 seconds) ``` If you get the above error, you might want to increase the size of the @@ -1108,18 +1120,45 @@ Custom configuration You can configure your own code through the Rails configuration object with custom configuration. It works like this: ```ruby - config.x.payment_processing.schedule = :daily - config.x.payment_processing.retries = 3 - config.x.super_debugger = true + config.payment_processing.schedule = :daily + config.payment_processing.retries = 3 + config.super_debugger = true ``` These configuration points are then available through the configuration object: ```ruby - Rails.configuration.x.payment_processing.schedule # => :daily - Rails.configuration.x.payment_processing.retries # => 3 - Rails.configuration.x.super_debugger # => true - Rails.configuration.x.super_debugger.not_set # => nil + Rails.configuration.payment_processing.schedule # => :daily + Rails.configuration.payment_processing.retries # => 3 + Rails.configuration.super_debugger # => true + Rails.configuration.super_debugger.not_set # => nil + ``` + +You can also use `Rails::Application.config_for` to load whole configuration files: + + ```ruby + # config/payment.yml: + production: + environment: production + merchant_id: production_merchant_id + public_key: production_public_key + private_key: production_private_key + development: + environment: sandbox + merchant_id: development_merchant_id + public_key: development_public_key + private_key: development_private_key + + # config/application.rb + module MyApp + class Application < Rails::Application + config.payment = config_for(:payment) + end + end + ``` + + ```ruby + Rails.configuration.payment['merchant_id'] # => production_merchant_id or development_merchant_id ``` Search Engines Indexing @@ -1141,3 +1180,25 @@ Disallow: / To block just specific pages, it's necessary to use a more complex syntax. Learn it on the [official documentation](http://www.robotstxt.org/robotstxt.html). + +Evented File System Monitor +--------------------------- + +If the [listen gem](https://github.com/guard/listen) is loaded Rails uses an +evented file system monitor to detect changes when `config.cache_classes` is +false: + +```ruby +group :development do + gem 'listen', '~> 3.0.4' +end +``` + +Otherwise, in every request Rails walks the application tree to check if +anything has changed. + +On Linux and Mac OS X no additional gems are needed, but some are required +[for *BSD](https://github.com/guard/listen#on-bsd) and +[for Windows](https://github.com/guard/listen#on-windows). + +Note that [some setups are unsupported](https://github.com/guard/listen#issues--limitations). diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md index 3b944f1274..0f98d12217 100644 --- a/guides/source/contributing_to_ruby_on_rails.md +++ b/guides/source/contributing_to_ruby_on_rails.md @@ -15,6 +15,9 @@ After reading this guide, you will know: Ruby on Rails is not "someone else's framework." Over the years, hundreds of people have contributed to Ruby on Rails ranging from a single character to massive architectural changes or significant documentation - all with the goal of making Ruby on Rails better for everyone. Even if you don't feel up to writing code or documentation yet, there are a variety of other ways that you can contribute, from reporting issues to testing patches. +As mentioned in [Rails +README](https://github.com/rails/rails/blob/master/README.md), everyone interacting in Rails and its sub-projects' codebases, issue trackers, chat rooms, and mailing lists is expected to follow the Rails [code of conduct](http://rubyonrails.org/conduct/). + -------------------------------------------------------------------------------- Reporting an Issue @@ -102,7 +105,7 @@ $ git checkout -b testing_branch Then you can use their remote branch to update your codebase. For example, let's say the GitHub user JohnSmith has forked and pushed to a topic branch "orange" located at https://github.com/JohnSmith/rails. ```bash -$ git remote add JohnSmith git://github.com/JohnSmith/rails.git +$ git remote add JohnSmith https://github.com/JohnSmith/rails.git $ git pull JohnSmith orange ``` @@ -125,11 +128,11 @@ Contributing to the Rails Documentation Ruby on Rails has two main sets of documentation: the guides, which help you learn about Ruby on Rails, and the API, which serves as a reference. -You can help improve the Rails guides by making them more coherent, consistent or readable, adding missing information, correcting factual errors, fixing typos, or bringing it up to date with the latest edge Rails. To get involved in the translation of Rails guides, please see [Translating Rails Guides](https://wiki.github.com/rails/docrails/translating-rails-guides). +You can help improve the Rails guides by making them more coherent, consistent or readable, adding missing information, correcting factual errors, fixing typos, or bringing them up to date with the latest edge Rails. You can either open a pull request to [Rails](http://github.com/rails/rails) or ask the [Rails core team](http://rubyonrails.org/core) for commit access on -[docrails](http://github.com/rails/docrails) if you contribute regularly. +docrails if you contribute regularly. Please do not open pull requests in docrails, if you'd like to get feedback on your change, ask for it in [Rails](http://github.com/rails/rails) instead. @@ -145,6 +148,42 @@ NOTE: To help our CI servers you should add [ci skip] to your documentation comm WARNING: Docrails has a very strict policy: no code can be touched whatsoever, no matter how trivial or small the change. Only RDoc and guides can be edited via docrails. Also, CHANGELOGs should never be edited in docrails. +Translating Rails Guides +------------------------ + +We are happy to have people volunteer to translate the Rails guides into their own language. +If you want to translate the Rails guides in your own language, follows these steps: + +* Fork the project (rails/rails). +* Add a source folder for your own language, for example: *guides/source/it-IT* for Italian. +* Copy the contents of *guides/source* into your own language directory and translate them. +* Do NOT translate the HTML files, as they are automatically generated. + +To generate the guides in HTML format cd into the *guides* directory then run (eg. for it-IT): + +```bash +$ bundle install +$ bundle exec rake guides:generate:html GUIDES_LANGUAGE=it-IT +``` + +This will generate the guides in an *output* directory. + +NOTE: The instructions are for Rails > 4. The Redcarpet Gem doesn't work with JRuby. + +Translation efforts we know about (various versions): + +* **Italian**: [https://github.com/rixlabs/docrails](https://github.com/rixlabs/docrails) +* **Spanish**: [http://wiki.github.com/gramos/docrails](http://wiki.github.com/gramos/docrails) +* **Polish**: [http://github.com/apohllo/docrails/tree/master](http://github.com/apohllo/docrails/tree/master) +* **French** : [http://github.com/railsfrance/docrails](http://github.com/railsfrance/docrails) +* **Czech** : [https://github.com/rubyonrails-cz/docrails/tree/czech](https://github.com/rubyonrails-cz/docrails/tree/czech) +* **Turkish** : [https://github.com/ujk/docrails/tree/master](https://github.com/ujk/docrails/tree/master) +* **Korean** : [https://github.com/rorlakr/rails-guides](https://github.com/rorlakr/rails-guides) +* **Simplified Chinese** : [https://github.com/ruby-china/guides](https://github.com/ruby-china/guides) +* **Traditional Chinese** : [https://github.com/docrails-tw/guides](https://github.com/docrails-tw/guides) +* **Russian** : [https://github.com/morsbox/rusrails](https://github.com/morsbox/rusrails) +* **Japanese** : [https://github.com/yasslab/railsguides.jp](https://github.com/yasslab/railsguides.jp) + Contributing to the Rails Code ------------------------------ @@ -165,7 +204,7 @@ In case you can't use the Rails development box, see [this other guide](developm To be able to contribute code, you need to clone the Rails repository: ```bash -$ git clone git://github.com/rails/rails.git +$ git clone https://github.com/rails/rails.git ``` and create a dedicated branch: @@ -315,10 +354,9 @@ $ cd activerecord $ bundle exec rake test:sqlite3 ``` -You can now run the tests as you did for `sqlite3`. The tasks are respectively +You can now run the tests as you did for `sqlite3`. The tasks are respectively: ```bash -test:mysql test:mysql2 test:postgresql ``` @@ -468,7 +506,7 @@ Navigate to the Rails [GitHub repository](https://github.com/rails/rails) and pr Add the new remote to your local repository on your local machine: ```bash -$ git remote add mine git@github.com:<your user name>/rails.git +$ git remote add mine https://github.com:<your user name>/rails.git ``` Push to your remote: @@ -482,7 +520,7 @@ You might have cloned your forked repository into your machine and might want to In the directory you cloned your fork: ```bash -$ git remote add rails git://github.com/rails/rails.git +$ git remote add rails https://github.com/rails/rails.git ``` Download new commits and branches from the official repository: diff --git a/guides/source/credits.html.erb b/guides/source/credits.html.erb index 61ea0b44ef..1d995581fa 100644 --- a/guides/source/credits.html.erb +++ b/guides/source/credits.html.erb @@ -28,7 +28,7 @@ Ruby on Rails Guides: Credits <h3 class="section">Rails Guides Authors</h3> <%= author('Ryan Bigg', 'radar', 'radar.png') do %> - Ryan Bigg works as the Community Manager at <a href="http://spreecommerce.com">Spree Commerce</a> and has been working with Rails since 2006. He's the author of <a href="https://leanpub.com/multi-tenancy-rails">Multi Tenancy With Rails</a> and co-author of <a href="http://manning.com/bigg2">Rails 4 in Action</a>. He's written many gems which can be seen on <a href="https://github.com/radar">his GitHub page</a> and he also tweets prolifically as <a href="http://twitter.com/ryanbigg">@ryanbigg</a>. + Ryan Bigg works as a Rails developer at <a href="http://marketplacer.com">Marketplacer</a> and has been working with Rails since 2006. He's the author of <a href="https://leanpub.com/multi-tenancy-rails">Multi Tenancy With Rails</a> and co-author of <a href="http://manning.com/bigg2">Rails 4 in Action</a>. He's written many gems which can be seen on <a href="https://github.com/radar">his GitHub page</a> and he also tweets prolifically as <a href="http://twitter.com/ryanbigg">@ryanbigg</a>. <% end %> <%= author('Oscar Del Ben', 'oscardelben', 'oscardelben.jpg') do %> diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md index dc1df8f229..35ad6eb705 100644 --- a/guides/source/debugging_rails_applications.md +++ b/guides/source/debugging_rails_applications.md @@ -311,14 +311,14 @@ processing the entire request. For example: ```bash -=> Booting WEBrick +=> Booting Puma => Rails 5.0.0 application starting in development on http://0.0.0.0:3000 => Run `rails server -h` for more startup options -=> Notice: server is listening on all interfaces (0.0.0.0). Consider using 127.0.0.1 (--binding option) => Ctrl-C to shutdown server -[2014-04-11 13:11:47] INFO WEBrick 1.3.1 -[2014-04-11 13:11:47] INFO ruby 2.2.2 (2015-04-13) [i686-linux] -[2014-04-11 13:11:47] INFO WEBrick::HTTPServer#start: pid=6370 port=3000 +Puma 2.15.3 starting... +* Min threads: 0, max threads: 16 +* Environment: development +* Listening on tcp://localhost:3000 Started GET "/" for 127.0.0.1 at 2014-04-11 13:11:48 +0200 @@ -346,23 +346,13 @@ by asking the debugger for help. Type: `help` ``` (byebug) help -byebug 2.7.0 + h[elp][ <cmd>[ <subcmd>]] -Type 'help <command-name>' for help on a specific command - -Available commands: -backtrace delete enable help list pry next restart source up -break disable eval info method ps save step var -catch display exit interrupt next putl set thread -condition down finish irb p quit show trace -continue edit frame kill pp reload skip undisplay + help -- prints this help. + help <cmd> -- prints help on command <cmd>. + help <cmd> <subcmd> -- prints help on <cmd>'s subcommand <subcmd>. ``` -TIP: To view the help menu for any command use `help <command-name>` at the -debugger prompt. For example: _`help list`_. You can abbreviate any debugging -command by supplying just enough letters to distinguish them from other -commands. For example, you can use `l` for the `list` command. - To see the previous ten lines you should type `list-` (or `l-`). ``` @@ -469,12 +459,12 @@ The debugger can list, stop, resume and switch between running threads by using the `thread` command (or the abbreviated `th`). This command has a handful of options: -* `thread` shows the current thread. -* `thread list` is used to list all threads and their statuses. The plus + +* `thread`: shows the current thread. +* `thread list`: is used to list all threads and their statuses. The plus + character and the number indicates the current thread of execution. -* `thread stop _n_` stop thread _n_. -* `thread resume _n_` resumes thread _n_. -* `thread switch _n_` switches the current thread context to _n_. +* `thread stop _n_`: stop thread _n_. +* `thread resume _n_`: resumes thread _n_. +* `thread switch _n_`: switches the current thread context to _n_. This command is very helpful when you are debugging concurrent threads and need to verify that there are no race conditions in your code. @@ -502,7 +492,7 @@ current context: (byebug) instance_variables [:@_action_has_layout, :@_routes, :@_headers, :@_status, :@_request, - :@_response, :@_env, :@_prefixes, :@_lookup_context, :@_action_name, + :@_response, :@_prefixes, :@_lookup_context, :@_action_name, :@_response_body, :@marked_for_same_origin_verification, :@_config] ``` @@ -533,7 +523,7 @@ And then ask again for the instance_variables: ``` (byebug) instance_variables [:@_action_has_layout, :@_routes, :@_headers, :@_status, :@_request, - :@_response, :@_env, :@_prefixes, :@_lookup_context, :@_action_name, + :@_response, :@_prefixes, :@_lookup_context, :@_action_name, :@_response_body, :@marked_for_same_origin_verification, :@_config, :@articles] ``` @@ -618,7 +608,7 @@ Started GET "/" for 127.0.0.1 at 2014-04-11 13:39:23 +0200 Processing by ArticlesController#index as HTML [1, 8] in /home/davidr/Proyectos/test_app/app/models/article.rb - 1: class Article < ActiveRecord::Base + 1: class Article < ApplicationRecord 2: 3: def self.find_recent(limit = 10) 4: byebug @@ -630,13 +620,16 @@ Processing by ArticlesController#index as HTML (byebug) ``` -If we use `next`, we want go deep inside method calls. Instead, byebug will go -to the next line within the same context. In this case, this is the last line of -the method, so `byebug` will jump to next next line of the previous frame. +If we use `next`, we won't go deep inside method calls. Instead, `byebug` will +go to the next line within the same context. In this case, it is the last line +of the current method, so `byebug` will return to the next line of the caller +method. ``` (byebug) next -Next went up a frame because previous frame finished + +Next advances to the next line (line 6: `end`), which returns to the next line +of the caller method: [4, 13] in /PathTo/project/test_app/app/controllers/articles_controller.rb 4: # GET /articles @@ -653,8 +646,8 @@ Next went up a frame because previous frame finished (byebug) ``` -If we use `step` in the same situation, we will literally go to the next Ruby -instruction to be executed. In this case, Active Support's `week` method. +If we use `step` in the same situation, `byebug` will literally go to the next +Ruby instruction to be executed -- in this case, Active Support's `week` method. ``` (byebug) step @@ -752,12 +745,12 @@ To list all active catchpoints use `catch`. There are two ways to resume execution of an application that is stopped in the debugger: -* `continue` [line-specification] \(or `c`): resume program execution, at the +* `continue [line-specification]` \(or `c`): resume program execution, at the address where your script last stopped; any breakpoints set at that address are bypassed. The optional argument line-specification allows you to specify a line number to set a one-time breakpoint which is deleted when that breakpoint is reached. -* `finish` [frame-number] \(or `fin`): execute until the selected stack frame +* `finish [frame-number]` \(or `fin`): execute until the selected stack frame returns. If no frame number is given, the application will run until the currently selected frame returns. The currently selected frame starts out the most-recent frame or 0 if no frame positioning (e.g up, down or frame) has been @@ -773,8 +766,8 @@ environment variable. A specific _line_ can also be given. ### Quitting -To exit the debugger, use the `quit` command (abbreviated `q`), or its alias -`exit`. +To exit the debugger, use the `quit` command (abbreviated to `q`). Or, type `q!` +to bypass the `Really quit? (y/n)` prompt and exit unconditionally. A simple quit tries to terminate all threads in effect. Therefore your server will be stopped and you will have to start it again. @@ -869,8 +862,8 @@ such as Valgrind. ### Valgrind -[Valgrind](http://valgrind.org/) is a Linux-only application for detecting -C-based memory leaks and race conditions. +[Valgrind](http://valgrind.org/) is an application for detecting C-based memory +leaks and race conditions. There are Valgrind tools that can automatically detect many memory management and threading bugs, and profile your programs in detail. For example, if a C diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md index 3c670a1221..7beb8f72a9 100644 --- a/guides/source/development_dependencies_install.md +++ b/guides/source/development_dependencies_install.md @@ -165,7 +165,7 @@ $ bundle exec ruby -Itest path/to/test.rb -n test_name ### Active Record Setup -The test suite of Active Record attempts to run four times: once for SQLite3, once for each of the two MySQL gems (`mysql` and `mysql2`), and once for PostgreSQL. We are going to see now how to set up the environment for them. +Active Record's test suite runs three times: once for SQLite3, once for MySQL, and once for PostgreSQL. We are going to see now how to set up the environment for them. WARNING: If you're working with Active Record code, you _must_ ensure that the tests pass for at least MySQL, PostgreSQL, and SQLite3. Subtle differences between the various adapters have been behind the rejection of many patches that looked OK when tested only against MySQL. @@ -190,7 +190,7 @@ Follow the instructions given by Homebrew to start these. In Ubuntu just run: ```bash -$ sudo apt-get install mysql-server libmysqlclient15-dev +$ sudo apt-get install mysql-server libmysqlclient-dev $ sudo apt-get install postgresql postgresql-client postgresql-contrib libpq-dev ``` diff --git a/guides/source/documents.yaml b/guides/source/documents.yaml index 9145aee009..fdd6d4d33d 100644 --- a/guides/source/documents.yaml +++ b/guides/source/documents.yaml @@ -122,6 +122,10 @@ url: autoloading_and_reloading_constants.html description: This guide documents how autoloading and reloading constants work. - + name: "Caching with Rails: An Overview" + url: caching_with_rails.html + description: This guide is an introduction to speeding up your Rails application with caching. + - name: Active Support Instrumentation work_in_progress: true url: active_support_instrumentation.html @@ -131,6 +135,11 @@ work_in_progress: true url: profiling.html description: This guide explains how to profile your Rails applications to improve performance. + - + name: Using Rails for API-only Applications + work_in_progress: true + url: api_app.html + description: This guide explains how to effectively use Rails to develop a JSON API application. - name: Extending Rails diff --git a/guides/source/engines.md b/guides/source/engines.md index 3b1588b75a..697938434c 100644 --- a/guides/source/engines.md +++ b/guides/source/engines.md @@ -150,7 +150,7 @@ When you include the engine into an application later on, you will do so with this line in the Rails application's `Gemfile`: ```ruby -gem 'blorgh', path: "vendor/engines/blorgh" +gem 'blorgh', path: 'engines/blorgh' ``` Don't forget to run `bundle install` as usual. By specifying it as a gem within @@ -239,6 +239,27 @@ NOTE: The `ApplicationController` class inside an engine is named just like a Rails application in order to make it easier for you to convert your applications into engines. +NOTE: Because of the way that Ruby does constant lookup you may run into a situation +where your engine controller is inheriting from the main application controller and +not your engine's application controller. Ruby is able to resolve the `ApplicationController` constant, and therefore the autoloading mechanism is not triggered. See the section [When Constants Aren't Missed](autoloading_and_reloading_constants.html#when-constants-aren-t-missed) of the [Autoloading and Reloading Constants](autoloading_and_reloading_constants.html) guide for further details. The best way to prevent this from +happening is to use `require_dependency` to ensure that the engine's application +controller is loaded. For example: + +``` ruby +# app/controllers/blorgh/articles_controller.rb: +require_dependency "blorgh/application_controller" + +module Blorgh + class ArticlesController < ApplicationController + ... + end +end +``` + +WARNING: Don't use `require` because it will break the automatic reloading of classes +in the development environment - using `require_dependency` ensures that classes are +loaded and unloaded in the correct manner. + Lastly, the `app/views` directory contains a `layouts` folder, which contains a file at `blorgh/application.html.erb`. This file allows you to specify a layout for the engine. If this engine is to be used as a stand-alone engine, then you @@ -402,7 +423,7 @@ Finally, the assets for this resource are generated in two files: `app/assets/stylesheets/blorgh/articles.css`. You'll see how to use these a little later. -You can see what the engine has so far by running `rake db:migrate` at the root +You can see what the engine has so far by running `bin/rails db:migrate` at the root of our engine to run the migration generated by the scaffold generator, and then running `rails server` in `test/dummy`. When you open `http://localhost:3000/blorgh/articles` you will see the default scaffold that has @@ -464,7 +485,7 @@ called `Blorgh::Comment`. Now run the migration to create our blorgh_comments table: ```bash -$ rake db:migrate +$ bin/rails db:migrate ``` To show the comments on an article, edit `app/views/blorgh/articles/show.html.erb` and @@ -487,7 +508,7 @@ Turning the model into this: ```ruby module Blorgh - class Article < ActiveRecord::Base + class Article < ApplicationRecord has_many :comments end end @@ -639,7 +660,7 @@ However, because you are developing the `blorgh` engine on your local machine, you will need to specify the `:path` option in your `Gemfile`: ```ruby -gem 'blorgh', path: "/path/to/blorgh" +gem 'blorgh', path: 'engines/blorgh' ``` Then run `bundle` to install the gem. @@ -670,17 +691,17 @@ pre-defined path which may be customizable. The engine contains migrations for the `blorgh_articles` and `blorgh_comments` table which need to be created in the application's database so that the engine's models can query them correctly. To copy these migrations into the -application use this command: +application run the following command from the `test/dummy` directory of your Rails engine: ```bash -$ rake blorgh:install:migrations +$ bin/rails blorgh:install:migrations ``` If you have multiple engines that need migrations copied over, use `railties:install:migrations` instead: ```bash -$ rake railties:install:migrations +$ bin/rails railties:install:migrations ``` This command, when run for the first time, will copy over all the migrations @@ -698,7 +719,7 @@ timestamp (`[timestamp_2]`) will be the current time plus a second. The reason for this is so that the migrations for the engine are run after any existing migrations in the application. -To run these migrations within the context of the application, simply run `rake +To run these migrations within the context of the application, simply run `bin/rails db:migrate`. When accessing the engine through `http://localhost:3000/blog`, the articles will be empty. This is because the table created inside the application is different from the one created within the engine. Go ahead, play around with the @@ -709,14 +730,14 @@ If you would like to run migrations only from one engine, you can do it by specifying `SCOPE`: ```bash -rake db:migrate SCOPE=blorgh +bin/rails db:migrate SCOPE=blorgh ``` This may be useful if you want to revert engine's migrations before removing it. To revert all migrations from blorgh engine you can run code such as: ```bash -rake db:migrate SCOPE=blorgh VERSION=0 +bin/rails db:migrate SCOPE=blorgh VERSION=0 ``` ### Using a Class Provided by the Application @@ -743,7 +764,7 @@ application: rails g model user name:string ``` -The `rake db:migrate` command needs to be run here to ensure that our +The `bin/rails db:migrate` command needs to be run here to ensure that our application has the `users` table for future use. Also, to keep it simple, the articles form will have a new text field called @@ -815,7 +836,7 @@ This migration will need to be run on the application. To do that, it must first be copied using this command: ```bash -$ rake blorgh:install:migrations +$ bin/rails blorgh:install:migrations ``` Notice that only _one_ migration was copied over here. This is because the first @@ -830,7 +851,7 @@ Copied migration [timestamp]_add_author_id_to_blorgh_articles.blorgh.rb from blo Run the migration using: ```bash -$ rake db:migrate +$ bin/rails db:migrate ``` Now with all the pieces in place, an action will take place that will associate @@ -843,28 +864,10 @@ above the "Title" output inside `app/views/blorgh/articles/show.html.erb`: ```html+erb <p> <b>Author:</b> - <%= @article.author %> + <%= @article.author.name %> </p> ``` -By outputting `@article.author` using the `<%=` tag, the `to_s` method will be -called on the object. By default, this will look quite ugly: - -``` -#<User:0x00000100ccb3b0> -``` - -This is undesirable. It would be much better to have the user's name there. To -do this, add a `to_s` method to the `User` class within the application: - -```ruby -def to_s - name -end -``` - -Now instead of the ugly Ruby object output, the author's name will be displayed. - #### Using a Controller Provided by the Application Because Rails controllers generally share code for things like authentication @@ -1030,9 +1033,9 @@ typical `GET` to a controller in a controller's functional test like this: ```ruby module Blorgh - class FooControllerTest < ActionController::TestCase + class FooControllerTest < ActionDispatch::IntegrationTest def test_index - get :index + get foos_url ... end end @@ -1046,13 +1049,13 @@ in your setup code: ```ruby module Blorgh - class FooControllerTest < ActionController::TestCase + class FooControllerTest < ActionDispatch::IntegrationTest setup do @routes = Engine.routes end def test_index - get :index + get foos_url ... end end @@ -1126,7 +1129,7 @@ end ```ruby # Blorgh/app/models/article.rb -class Article < ActiveRecord::Base +class Article < ApplicationRecord has_many :comments end ``` @@ -1147,7 +1150,7 @@ end ```ruby # Blorgh/app/models/article.rb -class Article < ActiveRecord::Base +class Article < ApplicationRecord has_many :comments def summary "#{title}" @@ -1168,7 +1171,7 @@ classes at run time allowing you to significantly modularize your code. ```ruby # MyApp/app/models/blorgh/article.rb -class Blorgh::Article < ActiveRecord::Base +class Blorgh::Article < ApplicationRecord include Blorgh::Concerns::Models::Article def time_since_created @@ -1184,7 +1187,7 @@ end ```ruby # Blorgh/app/models/article.rb -class Article < ActiveRecord::Base +class Article < ApplicationRecord include Blorgh::Concerns::Models::Article end ``` @@ -1351,7 +1354,7 @@ 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 `"blorgh/admin.css"` in its stylesheets. In this situation, you should explicitly define these assets for precompilation. This tells sprockets to add -your engine assets when `rake assets:precompile` is triggered. +your engine assets when `bin/rails assets:precompile` is triggered. You can define assets for precompilation in `engine.rb`: diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md index 8f7d97844e..2a289dd33a 100644 --- a/guides/source/form_helpers.md +++ b/guides/source/form_helpers.md @@ -40,7 +40,9 @@ When called without arguments like this, it creates a `<form>` tag which, when s </form> ``` -You'll notice that the HTML contains `input` element with type `hidden`. This `input` is important, because the form cannot be successfully submitted without it. The hidden input element has name attribute of `utf8` enforces browsers to properly respect your form's character encoding and is generated for all forms whether their actions are "GET" or "POST". The second input element with name `authenticity_token` is a security feature of Rails called **cross-site request forgery protection**, and form helpers generate it for every non-GET form (provided that this security feature is enabled). You can read more about this in the [Security Guide](security.html#cross-site-request-forgery-csrf). +You'll notice that the HTML contains an `input` element with type `hidden`. This `input` is important, because the form cannot be successfully submitted without it. The hidden input element with the name `utf8` enforces browsers to properly respect your form's character encoding and is generated for all forms whether their action is "GET" or "POST". + +The second input element with the name `authenticity_token` is a security feature of Rails called **cross-site request forgery protection**, and form helpers generate it for every non-GET form (provided that this security feature is enabled). You can read more about this in the [Security Guide](security.html#cross-site-request-forgery-csrf). ### A Generic Search Form @@ -103,9 +105,9 @@ checkboxes, text fields, and radio buttons. These basic helpers, with names ending in `_tag` (such as `text_field_tag` and `check_box_tag`), generate just a single `<input>` element. The first parameter to these is always the name of the input. When the form is submitted, the name will be passed along with the form -data, and will make its way to the `params` hash in the controller with the -value entered by the user for that field. For example, if the form contains `<%= -text_field_tag(:query) %>`, then you would be able to get the value of this +data, and will make its way to the `params` in the controller with the +value entered by the user for that field. For example, if the form contains +`<%= text_field_tag(:query) %>`, then you would be able to get the value of this field in the controller with `params[:query]`. When naming inputs, Rails uses certain conventions that make it possible to submit parameters with non-scalar values such as arrays or hashes, which will also be accessible in `params`. You can read more about them in [chapter 7 of this guide](#understanding-parameter-naming-conventions). For details on the precise usage of these helpers, please refer to the [API documentation](http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html). @@ -211,9 +213,8 @@ IMPORTANT: The search, telephone, date, time, color, datetime, datetime-local, month, week, URL, email, number and range inputs are HTML5 controls. If you require your app to have a consistent experience in older browsers, you will need an HTML5 polyfill (provided by CSS and/or JavaScript). -There is definitely [no shortage of solutions for this](https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills), although a couple of popular tools at the moment are -[Modernizr](http://www.modernizr.com/) and [yepnope](http://yepnopejs.com/), -which provide a simple way to add functionality based on the presence of +There is definitely [no shortage of solutions for this](https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills), although a popular tool at the moment is +[Modernizr](https://modernizr.com/), which provides a simple way to add functionality based on the presence of detected HTML5 features. TIP: If you're using password input fields (for any purpose), you might want to configure your application to prevent those parameters from being logged. You can learn about this in the [Security Guide](security.html#logging). @@ -377,7 +378,7 @@ output: </form> ``` -When parsing POSTed data, Rails will take into account the special `_method` parameter and acts as if the HTTP method was the one specified inside it ("PATCH" in this example). +When parsing POSTed data, Rails will take into account the special `_method` parameter and act as if the HTTP method was the one specified inside it ("PATCH" in this example). Making Select Boxes with Ease ----------------------------- @@ -656,7 +657,7 @@ NOTE: If the user has not selected a file the corresponding parameter will be an ### Dealing with Ajax -Unlike other forms making an asynchronous file upload form is not as simple as providing `form_for` with `remote: true`. With an Ajax form the serialization is done by JavaScript running inside the browser and since JavaScript cannot read files from your hard drive the file cannot be uploaded. The most common workaround is to use an invisible iframe that serves as the target for the form submission. +Unlike other forms, making an asynchronous file upload form is not as simple as providing `form_for` with `remote: true`. With an Ajax form the serialization is done by JavaScript running inside the browser and since JavaScript cannot read files from your hard drive the file cannot be uploaded. The most common workaround is to use an invisible iframe that serves as the target for the form submission. Customizing Form Builders ------------------------- @@ -712,13 +713,6 @@ action for a Person model, `params[:person]` would usually be a hash of all the Fundamentally HTML forms don't know about any sort of structured data, all they generate is name-value pairs, where pairs are just plain strings. The arrays and hashes you see in your application are the result of some parameter naming conventions that Rails uses. -TIP: You may find you can try out examples in this section faster by using the console to directly invoke Rack's parameter parser. For example, - -```ruby -Rack::Utils.parse_query "name=fred&phone=0123456789" -# => {"name"=>"fred", "phone"=>"0123456789"} -``` - ### Basic Structures The two basic structures are arrays and hashes. Hashes mirror the syntax used for accessing the value in `params`. For example, if a form contains: @@ -886,12 +880,12 @@ Many apps grow beyond simple forms editing a single object. For example, when cr Active Record provides model level support via the `accepts_nested_attributes_for` method: ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord has_many :addresses accepts_nested_attributes_for :addresses end -class Address < ActiveRecord::Base +class Address < ApplicationRecord belongs_to :person end ``` @@ -979,7 +973,7 @@ private You can allow users to delete associated objects by passing `allow_destroy: true` to `accepts_nested_attributes_for` ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord has_many :addresses accepts_nested_attributes_for :addresses, allow_destroy: true end @@ -1020,7 +1014,7 @@ end 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 +class Person < ApplicationRecord has_many :addresses accepts_nested_attributes_for :addresses, reject_if: lambda {|attributes| attributes['kind'].blank?} end diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index 400383cfb5..2cbc591629 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -23,10 +23,12 @@ application from scratch. It does not assume that you have any prior experience with Rails. However, to get the most out of it, you need to have some prerequisites installed: -* The [Ruby](https://www.ruby-lang.org/en/downloads) language version 2.2.2 or newer. -* Right version of [Development Kit](http://rubyinstaller.org/downloads/), if you are using Windows -* The [RubyGems](https://rubygems.org) packaging system, which is installed with Ruby - versions 1.9 and later. To learn more about RubyGems, please read the [RubyGems Guides](http://guides.rubygems.org). +* The [Ruby](https://www.ruby-lang.org/en/downloads) language version 2.2.2 or newer. +* Right version of [Development Kit](http://rubyinstaller.org/downloads/), if you + are using Windows. +* The [RubyGems](https://rubygems.org) packaging system, which is installed with + Ruby by default. To learn more about RubyGems, please read the + [RubyGems Guides](http://guides.rubygems.org). * A working installation of the [SQLite3 Database](https://www.sqlite.org). Rails is a web application framework running on the Ruby programming language. @@ -35,7 +37,7 @@ curve diving straight into Rails. There are several curated lists of online reso for learning Ruby: * [Official Ruby Programming Language website](https://www.ruby-lang.org/en/documentation/) -* [reSRC's List of Free Programming Books](http://resrc.io/list/10/list-of-free-programming-books/#ruby) +* [List of Free Programming Books](https://github.com/vhf/free-programming-books/blob/master/free-programming-books.md#ruby) Be aware that some resources, while still excellent, cover versions of Ruby as old as 1.6, and commonly 1.8, and will not include some syntax that you will see in day-to-day @@ -70,10 +72,9 @@ The Rails philosophy includes two major guiding principles: Creating a New Rails Project ---------------------------- - -The best way to use this guide is to follow each step as it happens, no code or -step needed to make this example application has been left out, so you can -literally follow along step by step. +The best way to read this guide is to follow it step by step. All steps are +essential to run this example application and no additional code or steps are +needed. By following along with this guide, you'll create a Rails project called `blog`, a (very) simple weblog. Before you can start building the application, @@ -90,17 +91,17 @@ Open up a command line prompt. On Mac OS X open Terminal.app, on Windows choose dollar sign `$` should be run in the command line. Verify that you have a current version of Ruby installed: +```bash +$ ruby -v +ruby 2.3.0p0 +``` + TIP: A number of tools exist to help you quickly install Ruby and Ruby on Rails on your system. Windows users can use [Rails Installer](http://railsinstaller.org), while Mac OS X users can use [Tokaido](https://github.com/tokaido/tokaidoapp). For more installation methods for most Operating Systems take a look at [ruby-lang.org](https://www.ruby-lang.org/en/documentation/installation/). -```bash -$ ruby -v -ruby 2.2.2p95 -``` - Many popular UNIX-like OSes ship with an acceptable version of SQLite3. On Windows, if you installed Rails through Rails Installer, you already have SQLite installed. Others can find installation instructions @@ -164,7 +165,7 @@ of the files and folders that Rails created by default: | File/Folder | Purpose | | ----------- | ------- | |app/|Contains the controllers, models, views, helpers, mailers and assets for your application. You'll focus on this folder for the remainder of this guide.| -|bin/|Contains the rails script that starts your app and can contain other scripts you use to setup, deploy or run your application.| +|bin/|Contains the rails script that starts your app and can contain other scripts you use to setup, update, deploy or run your application.| |config/|Configure your application's routes, database, and more. This is covered in more detail in [Configuring Rails Applications](configuring.html).| |config.ru|Rack configuration for Rack based servers used to start the application.| |db/|Contains your current database schema, as well as the database migrations.| @@ -173,7 +174,7 @@ of the files and folders that Rails created by default: |log/|Application log files.| |public/|The only folder seen by the world as-is. Contains static files and compiled assets.| |Rakefile|This file locates and loads tasks that can be run from the command line. The task definitions are defined throughout the components of Rails. Rather than changing Rakefile, you should add your own tasks by adding files to the lib/tasks directory of your application.| -|README.rdoc|This is a brief instruction manual for your application. You should edit this file to tell others what your application does, how to set it up, and so on.| +|README.md|This is a brief instruction manual for your application. You should edit this file to tell others what your application does, how to set it up, and so on.| |test/|Unit tests, fixtures, and other test apparatus. These are covered in [Testing Rails Applications](testing.html).| |tmp/|Temporary files (like cache and pid files).| |vendor/|A place for all third-party code. In a typical Rails application this includes vendored gems.| @@ -207,7 +208,7 @@ commented line for new apps and you can uncomment if you need it. default to the `Gemfile` in apps generated under JRuby. You can investigate all the supported runtimes at [ExecJS](https://github.com/rails/execjs#readme). -This will fire up WEBrick, a web server distributed with Ruby by default. To see +This will fire up Puma, a web server distributed with Rails by default. To see your application in action, open a browser window and navigate to <http://localhost:3000>. You should see the Rails default information page: @@ -299,7 +300,7 @@ Rails.application.routes.draw do # The priority is based upon order of creation: # first created -> highest priority. - # See how all your routes lay out with "rake routes". + # See how all your routes lay out with "bin/rails routes". # # You can have the root of your site routed with "root" # root 'welcome#index' @@ -358,13 +359,13 @@ Rails.application.routes.draw do end ``` -If you run `bin/rake routes`, you'll see that it has defined routes for all the +If you run `bin/rails routes`, you'll see that it has defined routes for all the standard RESTful actions. The meaning of the prefix column (and other columns) will be seen later, but for now notice that Rails has inferred the singular form `article` and makes meaningful use of the distinction. ```bash -$ bin/rake routes +$ bin/rails routes Prefix Verb URI Pattern Controller#Action articles GET /articles(.:format) articles#index POST /articles(.:format) articles#create @@ -558,10 +559,10 @@ this: In this example, the `articles_path` helper is passed to the `:url` option. To see what Rails will do with this, we look back at the output of -`bin/rake routes`: +`bin/rails routes`: ```bash -$ bin/rake routes +$ bin/rails routes Prefix Verb URI Pattern Controller#Action articles GET /articles(.:format) articles#index POST /articles(.:format) articles#create @@ -679,7 +680,7 @@ If you look in the `db/migrate/YYYYMMDDHHMMSS_create_articles.rb` file (remember, yours will have a slightly different name), here's what you'll find: ```ruby -class CreateArticles < ActiveRecord::Migration +class CreateArticles < ActiveRecord::Migration[5.0] def change create_table :articles do |t| t.string :title @@ -701,10 +702,10 @@ two timestamp fields to allow Rails to track article creation and update times. TIP: For more information about migrations, refer to [Rails Database Migrations] (migrations.html). -At this point, you can use a rake command to run the migration: +At this point, you can use a bin/rails command to run the migration: ```bash -$ bin/rake db:migrate +$ bin/rails db:migrate ``` Rails will execute this migration command and tell you it created the Articles @@ -721,7 +722,7 @@ NOTE. Because you're working in the development environment by default, this command will apply to the database defined in the `development` section of your `config/database.yml` file. If you would like to execute migrations in another environment, for instance in production, you must explicitly pass it when -invoking the command: `bin/rake db:migrate RAILS_ENV=production`. +invoking the command: `bin/rails db:migrate RAILS_ENV=production`. ### Saving data in the controller @@ -766,7 +767,7 @@ Why do you have to bother? The ability to grab and automatically assign all controller parameters to your model in one shot makes the programmer's job easier, but this convenience also allows malicious use. What if a request to the server was crafted to look like a new article form submit but also included -extra fields with values that violated your applications integrity? They would +extra fields with values that violated your application's integrity? They would be 'mass assigned' into your model and then into the database along with the good stuff - potentially breaking your application or worse. @@ -808,7 +809,7 @@ If you submit the form again now, Rails will complain about not finding the `show` action. That's not very useful though, so let's add the `show` action before proceeding. -As we have seen in the output of `bin/rake routes`, the route for `show` action is +As we have seen in the output of `bin/rails routes`, the route for `show` action is as follows: ``` @@ -870,7 +871,7 @@ Visit <http://localhost:3000/articles/new> and give it a try! ### Listing all articles We still need a way to list all our articles, so let's do that. -The route for this as per output of `bin/rake routes` is: +The route for this as per output of `bin/rails routes` is: ``` articles GET /articles(.:format) articles#index @@ -990,21 +991,22 @@ and restart the web server when a change is made. The model file, `app/models/article.rb` is about as simple as it can get: ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord end ``` There isn't much to this file - but note that the `Article` class inherits from -`ActiveRecord::Base`. Active Record supplies a great deal of functionality to -your Rails models for free, including basic database CRUD (Create, Read, Update, -Destroy) operations, data validation, as well as sophisticated search support -and the ability to relate multiple models to one another. +`ApplicationRecord`. `ApplicationRecord` inherits from `ActiveRecord::Base` +which supplies a great deal of functionality to your Rails models for free, +including basic database CRUD (Create, Read, Update, Destroy) operations, data +validation, as well as sophisticated search support and the ability to relate +multiple models to one another. Rails includes methods to help you validate the data that you send to models. Open the `app/models/article.rb` file and edit it: ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord validates :title, presence: true, length: { minimum: 5 } end @@ -1364,7 +1366,7 @@ Then do the same for the `app/views/articles/edit.html.erb` view: We're now ready to cover the "D" part of CRUD, deleting articles from the database. Following the REST convention, the route for -deleting articles as per output of `bin/rake routes` is: +deleting articles as per output of `bin/rails routes` is: ```ruby DELETE /articles/:id(.:format) articles#destroy @@ -1529,7 +1531,7 @@ This command will generate four files: First, take a look at `app/models/comment.rb`: ```ruby -class Comment < ActiveRecord::Base +class Comment < ApplicationRecord belongs_to :article end ``` @@ -1538,11 +1540,16 @@ This is very similar to the `Article` model that you saw earlier. The difference is the line `belongs_to :article`, which sets up an Active Record _association_. You'll learn a little about associations in the next section of this guide. +The (`:references`) keyword used in the bash command is a special data type for models. +It creates a new column on your database table with the provided model name appended with an `_id` +that can hold integer values. You can get a better understanding after analyzing the +`db/schema.rb` file below. + In addition to the model, Rails has also made a migration to create the corresponding database table: ```ruby -class CreateComments < ActiveRecord::Migration +class CreateComments < ActiveRecord::Migration[5.0] def change create_table :comments do |t| t.string :commenter @@ -1560,7 +1567,7 @@ for it, and a foreign key constraint that points to the `id` column of the `arti table. Go ahead and run the migration: ```bash -$ bin/rake db:migrate +$ bin/rails db:migrate ``` Rails is smart enough to only execute the migrations that have not already been @@ -1587,7 +1594,7 @@ association. You've already seen the line of code inside the `Comment` model (app/models/comment.rb) that makes each comment belong to an Article: ```ruby -class Comment < ActiveRecord::Base +class Comment < ApplicationRecord belongs_to :article end ``` @@ -1596,7 +1603,7 @@ You'll need to edit `app/models/article.rb` to add the other side of the association: ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord has_many :comments validates :title, presence: true, length: { minimum: 5 } @@ -1962,7 +1969,7 @@ you to use the `dependent` option of an association to achieve this. Modify the Article model, `app/models/article.rb`, as follows: ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord has_many :comments, dependent: :destroy validates :title, presence: true, length: { minimum: 5 } diff --git a/guides/source/i18n.md b/guides/source/i18n.md index 272a0e3623..5bbd4048b9 100644 --- a/guides/source/i18n.md +++ b/guides/source/i18n.md @@ -51,7 +51,7 @@ Thus, the Ruby I18n gem is split into two parts: As a user you should always only access the public methods on the I18n module, but it is useful to know about the capabilities of the backend. -NOTE: It is possible (or even desirable) to swap the shipped Simple backend with a more powerful one, which would store translation data in a relational database, GetText dictionary, or similar. See section [Using different backends](#using-different-backends) below. +NOTE: It is possible to swap the shipped Simple backend with a more powerful one, which would store translation data in a relational database, GetText dictionary, or similar. See section [Using different backends](#using-different-backends) below. ### The Public I18n API @@ -109,7 +109,7 @@ The **translations load path** (`I18n.load_path`) is an array of paths to files NOTE: The backend lazy-loads these translations when a translation is looked up for the first time. This backend can be swapped with something else even after translations have already been announced. -The default `application.rb` file has instructions on how to add locales from another directory and how to set a different default locale. +The default `config/application.rb` file has instructions on how to add locales from another directory and how to set a different default locale. ```ruby # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. @@ -117,7 +117,7 @@ The default `application.rb` file has instructions on how to add locales from an # config.i18n.default_locale = :de ``` -The load path must be specified before any translations are looked up. To change the default locale from an initializer instead of `application.rb`: +The load path must be specified before any translations are looked up. To change the default locale from an initializer instead of `config/application.rb`: ```ruby # config/initializers/locale.rb @@ -249,7 +249,7 @@ end With this approach you will not get a `Routing Error` when accessing your resources such as `http://localhost:3001/books` without a locale. This is useful for when you want to use the default locale when one is not specified. -Of course, you need to take special care of the root URL (usually "homepage" or "dashboard") of your application. An URL like `http://localhost:3001/nl` will not work automatically, because the `root to: "books#index"` declaration in your `routes.rb` doesn't take locale into account. (And rightly so: there's only one "root" URL.) +Of course, you need to take special care of the root URL (usually "homepage" or "dashboard") of your application. A URL like `http://localhost:3001/nl` will not work automatically, because the `root to: "books#index"` declaration in your `routes.rb` doesn't take locale into account. (And rightly so: there's only one "root" URL.) You would probably need to map URLs like these: @@ -470,7 +470,7 @@ OK! Now let's add a timestamp to the view, so we can demo the **date/time locali ```erb # app/views/home/index.html.erb <h1><%=t :hello_world %></h1> -<p><%= flash[:notice] %></p +<p><%= flash[:notice] %></p> <p><%= l Time.now, format: :short %></p> ``` @@ -805,6 +805,8 @@ en: Then `User.human_attribute_name("gender.female")` will return "Female". +NOTE: If you are using a class which includes `ActiveModel` and does not inherit from `ActiveRecord::Base`, replace `activerecord` with `activemodel` in the above key paths. + #### Error Message Scopes Active Record validation error messages can also be translated easily. Active Record gives you a couple of namespaces where you can place your message translations in order to provide different messages and translation for certain models, attributes, and/or validations. It also transparently takes single table inheritance into account. @@ -814,7 +816,7 @@ This gives you quite powerful means to flexibly adjust your messages to your app Consider a User model with a validation for the name attribute like this: ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord validates :name, presence: true end ``` diff --git a/guides/source/initialization.md b/guides/source/initialization.md index 43083ebb86..156f9c92b4 100644 --- a/guides/source/initialization.md +++ b/guides/source/initialization.md @@ -86,10 +86,9 @@ The `APP_PATH` constant will be used later in `rails/commands`. The `config/boot `config/boot.rb` contains: ```ruby -# Set up gems listed in the Gemfile. ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) -require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) +require 'bundler/setup' # Set up gems listed in the Gemfile. ``` In a standard Rails application, there's a `Gemfile` which declares all @@ -140,7 +139,8 @@ aliases = { "c" => "console", "s" => "server", "db" => "dbconsole", - "r" => "runner" + "r" => "runner", + "t" => "test" } command = ARGV.shift @@ -157,21 +157,22 @@ snippet. If we had used `s` rather than `server`, Rails would have used the `aliases` defined here to find the matching command. -### `rails/commands/command_tasks.rb` +### `rails/commands/commands_tasks.rb` -When one types an incorrect rails command, the `run_command` is responsible for -throwing an error message. If the command is valid, a method of the same name -is called. +When one types a valid Rails command, `run_command!` a method of the same name +is called. If Rails doesn't recognize the command, it tries to run a Rake task +of the same name. ```ruby COMMAND_WHITELIST = %w(plugin generate destroy console server dbconsole application runner new version help) def run_command!(command) command = parse_command(command) + if COMMAND_WHITELIST.include?(command) send(command) else - write_error_message(command) + run_rake_task(command) end end ``` @@ -529,16 +530,17 @@ This file is responsible for requiring all the individual frameworks of Rails: require "rails" %w( - active_record - action_controller - action_view - action_mailer - active_job - rails/test_unit - sprockets -).each do |framework| + active_record/railtie + action_controller/railtie + action_view/railtie + action_mailer/railtie + active_job/railtie + action_cable/engine + rails/test_unit/railtie + sprockets/railtie +).each do |railtie| begin - require "#{framework}/railtie" + require "#{railtie}" rescue LoadError end end diff --git a/guides/source/kindle/layout.html.erb b/guides/source/kindle/layout.html.erb index f0a286210b..fd8746776b 100644 --- a/guides/source/kindle/layout.html.erb +++ b/guides/source/kindle/layout.html.erb @@ -14,12 +14,12 @@ <% if content_for? :header_section %> <%= yield :header_section %> - <div class="pagebreak"> + <div class="pagebreak"></div> <% end %> <% if content_for? :index_section %> <%= yield :index_section %> - <div class="pagebreak"> + <div class="pagebreak"></div> <% end %> <%= yield.html_safe %> diff --git a/guides/source/kindle/toc.ncx.erb b/guides/source/kindle/toc.ncx.erb index 2c6d8e3bdf..5094fea4ca 100644 --- a/guides/source/kindle/toc.ncx.erb +++ b/guides/source/kindle/toc.ncx.erb @@ -32,12 +32,12 @@ </navPoint> <navPoint class="article" id="credits" playOrder="3"> <navLabel><text>Credits</text></navLabel> - <content src="credits.html"> + <content src="credits.html"/> </navPoint> <navPoint class="article" id="copyright" playOrder="4"> <navLabel><text>Copyright & License</text></navLabel> - <content src="copyright.html"> - </navPoint> + <content src="copyright.html"/> + </navPoint> </navPoint> <% play_order = 4 %> @@ -47,7 +47,7 @@ <text><%= section['name'] %></text> </navLabel> <content src="<%=section['documents'].first['url'] %>"/> - + <% section['documents'].each_with_index do |document, document_no| %> <navPoint class="article" id="_<%=section_no+1%>.<%=document_no+1%>" playOrder="<%=play_order +=1 %>"> <navLabel> diff --git a/guides/source/layout.html.erb b/guides/source/layout.html.erb index 1005057ca9..1f81ea4694 100644 --- a/guides/source/layout.html.erb +++ b/guides/source/layout.html.erb @@ -24,20 +24,7 @@ <% end %> <div id="topNav"> <div class="wrapper"> - <strong class="more-info-label">More at <a href="http://rubyonrails.org/">rubyonrails.org:</a> </strong> - <span class="red-button more-info-button"> - More Ruby on Rails - </span> - <ul class="more-info-links s-hidden"> - <li class="more-info"><a href="http://rubyonrails.org/">Overview</a></li> - <li class="more-info"><a href="http://rubyonrails.org/download">Download</a></li> - <li class="more-info"><a href="http://rubyonrails.org/deploy">Deploy</a></li> - <li class="more-info"><a href="https://github.com/rails/rails">Code</a></li> - <li class="more-info"><a href="http://rubyonrails.org/screencasts">Screencasts</a></li> - <li class="more-info"><a href="http://rubyonrails.org/documentation">Documentation</a></li> - <li class="more-info"><a href="http://rubyonrails.org/community">Community</a></li> - <li class="more-info"><a href="http://weblog.rubyonrails.org/">Blog</a></li> - </ul> + <strong class="more-info-label">←<a href="http://rubyonrails.org/">Back to rubyonrails.org:</a> </strong> </div> </div> <div id="header"> diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md index 94cd7297e2..d55e1007ee 100644 --- a/guides/source/layouts_and_rendering.md +++ b/guides/source/layouts_and_rendering.md @@ -238,7 +238,7 @@ TIP: This is useful when you're rendering a small snippet of HTML code. However, you might want to consider moving it to a template file if the markup is complex. -NOTE: This option will escape HTML entities if the string is not HTML safe. +NOTE: When using `html:` option, HTML entities will be escaped if the string is not marked as HTML safe by using `html_safe` method. #### Rendering JSON @@ -280,7 +280,7 @@ render body: "raw" ``` TIP: This option should be used only if you don't care about the content type of -the response. Using `:plain` or `:html` might be more appropriate in most of the +the response. Using `:plain` or `:html` might be more appropriate most of the time. NOTE: Unless overridden, your response returned from this render option will be @@ -360,7 +360,6 @@ Rails understands both numeric status codes and the corresponding symbols shown | | 303 | :see_other | | | 304 | :not_modified | | | 305 | :use_proxy | -| | 306 | :reserved | | | 307 | :temporary_redirect | | | 308 | :permanent_redirect | | **Client Error** | 400 | :bad_request | @@ -376,10 +375,10 @@ Rails understands both numeric status codes and the corresponding symbols shown | | 410 | :gone | | | 411 | :length_required | | | 412 | :precondition_failed | -| | 413 | :request_entity_too_large | -| | 414 | :request_uri_too_long | +| | 413 | :payload_too_large | +| | 414 | :uri_too_long | | | 415 | :unsupported_media_type | -| | 416 | :requested_range_not_satisfiable | +| | 416 | :range_not_satisfiable | | | 417 | :expectation_failed | | | 422 | :unprocessable_entity | | | 423 | :locked | @@ -623,10 +622,13 @@ Another way to handle returning responses to an HTTP request is with `redirect_t redirect_to photos_url ``` -You can use `redirect_to` with any arguments that you could use with `link_to` or `url_for`. There's also a special redirect that sends the user back to the page they just came from: +You can use `redirect_back` to return the user to the page they just came from. +This location is pulled from the `HTTP_REFERER` header which is not guaranteed +to be set by the browser, so you must provide the `fallback_location` +to use in this case. ```ruby -redirect_to :back +redirect_back(fallback_location: root_path) ``` #### Getting a Different Redirect Status Code @@ -782,7 +784,7 @@ The `javascript_include_tag` helper returns an HTML `script` tag for each source If you are using Rails with the [Asset Pipeline](asset_pipeline.html) enabled, this helper will generate a link to `/assets/javascripts/` rather than `public/javascripts` which was used in earlier versions of Rails. This link is then served by the asset pipeline. -A JavaScript file within a Rails application or Rails engine goes in one of three locations: `app/assets`, `lib/assets` or `vendor/assets`. These locations are explained in detail in the [Asset Organization section in the Asset Pipeline Guide](asset_pipeline.html#asset-organization) +A JavaScript file within a Rails application or Rails engine goes in one of three locations: `app/assets`, `lib/assets` or `vendor/assets`. These locations are explained in detail in the [Asset Organization section in the Asset Pipeline Guide](asset_pipeline.html#asset-organization). You can specify a full path relative to the document root, or a URL, if you prefer. For example, to link to a JavaScript file that is inside a directory called `javascripts` inside of one of `app/assets`, `lib/assets` or `vendor/assets`, you would do this: @@ -1155,14 +1157,12 @@ To pass a local variable to a partial in only specific cases use the `local_assi * `_articles.html.erb` ```erb - <%= content_tag_for :article, article do |article| %> - <h2><%= article.title %></h2> + <h2><%= article.title %></h2> - <% if local_assigns[:full] %> - <%= simple_format article.body %> - <% else %> - <%= truncate article.body %> - <% end %> + <% if local_assigns[:full] %> + <%= simple_format article.body %> + <% else %> + <%= truncate article.body %> <% end %> ``` diff --git a/guides/source/maintenance_policy.md b/guides/source/maintenance_policy.md index 50308f505a..f99b6ebd31 100644 --- a/guides/source/maintenance_policy.md +++ b/guides/source/maintenance_policy.md @@ -44,7 +44,7 @@ from. In special situations, where someone from the Core Team agrees to support more series, they are included in the list of supported series. -**Currently included series:** `4.2.Z`, `4.1.Z` (Supported by Rafael França). +**Currently included series:** `5.0.Z`. Security Issues --------------- @@ -59,7 +59,7 @@ be built from 1.2.2, and then added to the end of 1-2-stable. This means that security releases are easy to upgrade to if you're running the latest version of Rails. -**Currently included series:** `4.2.Z`, `4.1.Z`. +**Currently included series:** `5.0.Z`, `4.2.Z`. Severe Security Issues ---------------------- @@ -68,7 +68,7 @@ For severe security issues we will provide new versions as above, and also the last major release series will receive patches and new versions. The classification of the security issue is judged by the core team. -**Currently included series:** `4.2.Z`, `4.1.Z`, `3.2.Z`. +**Currently included series:** `5.0.Z`, `4.2.Z`. Unsupported Release Series -------------------------- diff --git a/guides/source/nested_model_forms.md b/guides/source/nested_model_forms.md index 121cf2b185..71efa4b0d0 100644 --- a/guides/source/nested_model_forms.md +++ b/guides/source/nested_model_forms.md @@ -32,7 +32,7 @@ For an ActiveRecord::Base model and association this writer method is commonly d #### has_one ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord has_one :address accepts_nested_attributes_for :address end @@ -41,7 +41,7 @@ end #### belongs_to ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord belongs_to :firm accepts_nested_attributes_for :firm end @@ -50,7 +50,7 @@ end #### has_many / has_and_belongs_to_many ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord has_many :projects accepts_nested_attributes_for :projects end diff --git a/guides/source/plugins.md b/guides/source/plugins.md index 4e630a39f3..8f055f8fe3 100644 --- a/guides/source/plugins.md +++ b/guides/source/plugins.md @@ -17,7 +17,7 @@ After reading this guide, you will know: This guide describes how to build a test-driven plugin that will: * Extend core Ruby classes like Hash and String. -* Add methods to `ActiveRecord::Base` in the tradition of the `acts_as` plugins. +* Add methods to `ApplicationRecord` in the tradition of the `acts_as` plugins. * Give you information about where to put generators in your plugin. For the purpose of this guide pretend for a moment that you are an avid bird watcher. @@ -37,7 +37,7 @@ different rails applications using RubyGems and Bundler if desired. Rails ships with a `rails plugin new` command which creates a skeleton for developing any kind of Rails extension with the ability -to run integration tests using a dummy Rails application. Create your +to run integration tests using a dummy Rails application. Create your plugin with the command: ```bash @@ -54,7 +54,7 @@ Testing Your Newly Generated Plugin ----------------------------------- You can navigate to the directory that contains the plugin, run the `bundle install` command - and run the one generated test using the `rake` command. + and run the one generated test using the `bin/test` command. You should see: @@ -83,13 +83,23 @@ class CoreExtTest < ActiveSupport::TestCase end ``` -Run `rake` to run the test. This test should fail because we haven't implemented the `to_squawk` method: +Run `bin/test` to run the test. This test should fail because we haven't implemented the `to_squawk` method: ```bash - 1) Error: - CoreExtTest#test_to_squawk_prepends_the_word_squawk: - NoMethodError: undefined method `to_squawk' for "Hello World":String - /path/to/yaffle/test/core_ext_test.rb:5:in `test_to_squawk_prepends_the_word_squawk' +E + +Error: +CoreExtTest#test_to_squawk_prepends_the_word_squawk: +NoMethodError: undefined method `to_squawk' for "Hello World":String + + +bin/test /path/to/yaffle/test/core_ext_test.rb:4 + +. + +Finished in 0.003358s, 595.6483 runs/s, 297.8242 assertions/s. + +2 runs, 1 assertions, 0 failures, 1 errors, 0 skips ``` Great - now you are ready to start development. @@ -117,7 +127,7 @@ String.class_eval do end ``` -To test that your method does what it says it does, run the unit tests with `rake` from your plugin directory. +To test that your method does what it says it does, run the unit tests with `bin/test` from your plugin directory. ```bash 2 runs, 2 assertions, 0 failures, 0 errors, 0 skips @@ -182,7 +192,6 @@ To start out, write a failing test that shows the behavior you'd like: require 'test_helper' class ActsAsYaffleTest < ActiveSupport::TestCase - def test_a_hickwalls_yaffle_text_field_should_be_last_squawk assert_equal "last_squawk", Hickwall.yaffle_text_field end @@ -190,24 +199,37 @@ class ActsAsYaffleTest < ActiveSupport::TestCase def test_a_wickwalls_yaffle_text_field_should_be_last_tweet assert_equal "last_tweet", Wickwall.yaffle_text_field end - end ``` -When you run `rake`, you should see the following: +When you run `bin/test`, you should see the following: ``` - 1) Error: - ActsAsYaffleTest#test_a_hickwalls_yaffle_text_field_should_be_last_squawk: - NameError: uninitialized constant ActsAsYaffleTest::Hickwall - /path/to/yaffle/test/acts_as_yaffle_test.rb:6:in `test_a_hickwalls_yaffle_text_field_should_be_last_squawk' +# Running: + +..E + +Error: +ActsAsYaffleTest#test_a_wickwalls_yaffle_text_field_should_be_last_tweet: +NameError: uninitialized constant ActsAsYaffleTest::Wickwall - 2) Error: - ActsAsYaffleTest#test_a_wickwalls_yaffle_text_field_should_be_last_tweet: - NameError: uninitialized constant ActsAsYaffleTest::Wickwall - /path/to/yaffle/test/acts_as_yaffle_test.rb:10:in `test_a_wickwalls_yaffle_text_field_should_be_last_tweet' - 4 runs, 2 assertions, 0 failures, 2 errors, 0 skips +bin/test /path/to/yaffle/test/acts_as_yaffle_test.rb:8 + +E + +Error: +ActsAsYaffleTest#test_a_hickwalls_yaffle_text_field_should_be_last_squawk: +NameError: uninitialized constant ActsAsYaffleTest::Hickwall + + +bin/test /path/to/yaffle/test/acts_as_yaffle_test.rb:4 + + + +Finished in 0.004812s, 831.2949 runs/s, 415.6475 assertions/s. + +4 runs, 2 assertions, 0 failures, 2 errors, 0 skips ``` This tells us that we don't have the necessary models (Hickwall and Wickwall) that we are trying to test. @@ -225,7 +247,7 @@ and migrating the database. First, run: ```bash $ cd test/dummy -$ bin/rake db:migrate +$ bin/rails db:migrate ``` While you are here, change the Hickwall and Wickwall models so that they know that they are supposed to act @@ -234,22 +256,22 @@ like yaffles. ```ruby # test/dummy/app/models/hickwall.rb -class Hickwall < ActiveRecord::Base +class Hickwall < ApplicationRecord acts_as_yaffle end # test/dummy/app/models/wickwall.rb -class Wickwall < ActiveRecord::Base +class Wickwall < ApplicationRecord acts_as_yaffle yaffle_text_field: :last_tweet end - ``` We will also add code to define the `acts_as_yaffle` method. ```ruby # yaffle/lib/yaffle/acts_as_yaffle.rb + module Yaffle module ActsAsYaffle extend ActiveSupport::Concern @@ -265,26 +287,43 @@ module Yaffle end end -ActiveRecord::Base.include(Yaffle::ActsAsYaffle) +# test/dummy/app/models/application_record.rb + +class ApplicationRecord < ActiveRecord::Base + include Yaffle::ActsAsYaffle + + self.abstract_class = true +end ``` -You can then return to the root directory (`cd ../..`) of your plugin and rerun the tests using `rake`. +You can then return to the root directory (`cd ../..`) of your plugin and rerun the tests using `bin/test`. ``` - 1) Error: - ActsAsYaffleTest#test_a_hickwalls_yaffle_text_field_should_be_last_squawk: - NoMethodError: undefined method `yaffle_text_field' for #<Class:0x007fd105e3b218> - activerecord (4.1.5) lib/active_record/dynamic_matchers.rb:26:in `method_missing' - /path/to/yaffle/test/acts_as_yaffle_test.rb:6:in `test_a_hickwalls_yaffle_text_field_should_be_last_squawk' +# Running: + +.E + +Error: +ActsAsYaffleTest#test_a_hickwalls_yaffle_text_field_should_be_last_squawk: +NoMethodError: undefined method `yaffle_text_field' for #<Class:0x0055974ebbe9d8> + + +bin/test /path/to/yaffle/test/acts_as_yaffle_test.rb:4 + +E + +Error: +ActsAsYaffleTest#test_a_wickwalls_yaffle_text_field_should_be_last_tweet: +NoMethodError: undefined method `yaffle_text_field' for #<Class:0x0055974eb8cfc8> + - 2) Error: - ActsAsYaffleTest#test_a_wickwalls_yaffle_text_field_should_be_last_tweet: - NoMethodError: undefined method `yaffle_text_field' for #<Class:0x007fd105e409c0> - activerecord (4.1.5) lib/active_record/dynamic_matchers.rb:26:in `method_missing' - /path/to/yaffle/test/acts_as_yaffle_test.rb:10:in `test_a_wickwalls_yaffle_text_field_should_be_last_tweet' +bin/test /path/to/yaffle/test/acts_as_yaffle_test.rb:8 - 4 runs, 2 assertions, 0 failures, 2 errors, 0 skips +. +Finished in 0.008263s, 484.0999 runs/s, 242.0500 assertions/s. + +4 runs, 2 assertions, 0 failures, 2 errors, 0 skips ``` Getting closer... Now we will implement the code of the `acts_as_yaffle` method to make the tests pass. @@ -294,7 +333,7 @@ Getting closer... Now we will implement the code of the `acts_as_yaffle` method module Yaffle module ActsAsYaffle - extend ActiveSupport::Concern + extend ActiveSupport::Concern included do end @@ -308,10 +347,16 @@ module Yaffle end end -ActiveRecord::Base.include(Yaffle::ActsAsYaffle) +# test/dummy/app/models/application_record.rb + +class ApplicationRecord < ActiveRecord::Base + include Yaffle::ActsAsYaffle + + self.abstract_class = true +end ``` -When you run `rake`, you should see the tests all pass: +When you run `bin/test`, you should see the tests all pass: ```bash 4 runs, 4 assertions, 0 failures, 0 errors, 0 skips @@ -329,7 +374,6 @@ To start out, write a failing test that shows the behavior you'd like: require 'test_helper' class ActsAsYaffleTest < ActiveSupport::TestCase - def test_a_hickwalls_yaffle_text_field_should_be_last_squawk assert_equal "last_squawk", Hickwall.yaffle_text_field end @@ -382,10 +426,16 @@ module Yaffle end end -ActiveRecord::Base.include(Yaffle::ActsAsYaffle) +# test/dummy/app/models/application_record.rb + +class ApplicationRecord < ActiveRecord::Base + include Yaffle::ActsAsYaffle + + self.abstract_class = true +end ``` -Run `rake` one final time and you should see: +Run `bin/test` one final time and you should see: ``` 6 runs, 6 assertions, 0 failures, 0 errors, 0 skips @@ -443,4 +493,3 @@ $ bundle exec rake rdoc * [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://guides.rubygems.org/specification-reference/) -* [GemPlugins: A Brief Introduction to the Future of Rails Plugins](http://www.intridea.com/blog/2008/6/11/gemplugins-a-brief-introduction-to-the-future-of-rails-plugins) diff --git a/guides/source/rails_application_templates.md b/guides/source/rails_application_templates.md index b7364536c3..5a46baff2d 100644 --- a/guides/source/rails_application_templates.md +++ b/guides/source/rails_application_templates.md @@ -25,8 +25,8 @@ $ rails new blog -m http://example.com/template.rb You can use the rake task `rails:template` to apply templates to an existing Rails application. The location of the template needs to be passed in to an environment variable named LOCATION. Again, this can either be path to a file or a URL. ```bash -$ bin/rake rails:template LOCATION=~/template.rb -$ bin/rake rails:template LOCATION=http://example.com/template.rb +$ bin/rails rails:template LOCATION=~/template.rb +$ bin/rails rails:template LOCATION=http://example.com/template.rb ``` Template API @@ -78,7 +78,7 @@ gem_group :development, :test do end ``` -### add_source(source, options = {}) +### add_source(source, options={}, &block) Adds the given source to the generated application's `Gemfile`. @@ -88,6 +88,14 @@ For example, if you need to source a gem from `"http://code.whytheluckystiff.net add_source "http://code.whytheluckystiff.net" ``` +If block is given, gem entries in block are wrapped into the source group. + +```ruby +add_source "http://gems.github.com/" do + gem "rspec-rails" +end +``` + ### environment/application(data=nil, options={}, &block) Adds a line inside the `Application` class for `config/application.rb`. diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md index 1e2fe94010..3b61d65df5 100644 --- a/guides/source/rails_on_rack.md +++ b/guides/source/rails_on_rack.md @@ -96,7 +96,7 @@ NOTE: `ActionDispatch::MiddlewareStack` is Rails equivalent of `Rack::Builder`, Rails has a handy rake task for inspecting the middleware stack in use: ```bash -$ bin/rake middleware +$ bin/rails middleware ``` For a freshly generated Rails application, this might produce something like: @@ -104,7 +104,7 @@ For a freshly generated Rails application, this might produce something like: ```ruby use Rack::Sendfile use ActionDispatch::Static -use Rack::Lock +use ActionDispatch::LoadInterlock use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x000000029a0838> use Rack::Runtime use Rack::MethodOverride @@ -121,7 +121,6 @@ use ActiveRecord::QueryCache use ActionDispatch::Cookies use ActionDispatch::Session::CookieStore use ActionDispatch::Flash -use ActionDispatch::ParamsParser use Rack::Head use Rack::ConditionalGet use Rack::ETag @@ -172,14 +171,14 @@ Add the following lines to your application configuration: ```ruby # config/application.rb -config.middleware.delete "Rack::Lock" +config.middleware.delete Rack::Runtime ``` -And now if you inspect the middleware stack, you'll find that `Rack::Lock` is +And now if you inspect the middleware stack, you'll find that `Rack::Runtime` is not a part of it. ```bash -$ bin/rake middleware +$ bin/rails middleware (in /Users/lifo/Rails/blog) use ActionDispatch::Static use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x00000001c304c8> @@ -192,16 +191,16 @@ If you want to remove session related middleware, do the following: ```ruby # config/application.rb -config.middleware.delete "ActionDispatch::Cookies" -config.middleware.delete "ActionDispatch::Session::CookieStore" -config.middleware.delete "ActionDispatch::Flash" +config.middleware.delete ActionDispatch::Cookies +config.middleware.delete ActionDispatch::Session::CookieStore +config.middleware.delete ActionDispatch::Flash ``` And to remove browser related middleware, ```ruby # config/application.rb -config.middleware.delete "Rack::MethodOverride" +config.middleware.delete Rack::MethodOverride ``` ### Internal Middleware Stack @@ -214,12 +213,16 @@ Much of Action Controller's functionality is implemented as Middlewares. The fol **`ActionDispatch::Static`** -* Used to serve static files. Disabled if `config.serve_static_files` is `false`. +* Used to serve static files from the public directory. Disabled if `config.public_file_server.enabled` is `false`. **`Rack::Lock`** * Sets `env["rack.multithread"]` flag to `false` and wraps the application within a Mutex. +**`ActionDispatch::LoadInterlock`** + +* Used for thread safe code reloading during development. + **`ActiveSupport::Cache::Strategy::LocalCache::Middleware`** * Used for memory caching. This cache is not thread safe. @@ -284,10 +287,6 @@ Much of Action Controller's functionality is implemented as Middlewares. The fol * Sets up the flash keys. Only available if `config.action_controller.session_store` is set to a value. -**`ActionDispatch::ParamsParser`** - -* Parses out parameters from the request into `params`. - **`Rack::Head`** * Converts HEAD requests to `GET` requests and serves them as so. diff --git a/guides/source/routing.md b/guides/source/routing.md index 52f11f92bd..d9e64d56ac 100644 --- a/guides/source/routing.md +++ b/guides/source/routing.md @@ -7,7 +7,7 @@ This guide covers the user-facing features of Rails routing. After reading this guide, you will know: -* How to interpret the code in `routes.rb`. +* How to interpret the code in `config/routes.rb`. * How to construct your own routes, using either the preferred resourceful style or the `match` method. * What parameters to expect an action to receive. * How to automatically create paths and URLs using route helpers. @@ -79,11 +79,13 @@ it asks the router to map it to a controller action. If the first matching route resources :photos ``` -Rails would dispatch that request to the `destroy` method on the `photos` controller with `{ id: '17' }` in `params`. +Rails would dispatch that request to the `destroy` action on the `photos` controller with `{ id: '17' }` in `params`. ### CRUD, Verbs, and Actions -In Rails, a resourceful route provides a mapping between HTTP verbs and URLs to controller actions. By convention, each action also maps to particular CRUD operations in a database. A single entry in the routing file, such as: +In Rails, a resourceful route provides a mapping between HTTP verbs and URLs to +controller actions. By convention, each action also maps to a specific CRUD +operation in a database. A single entry in the routing file, such as: ```ruby resources :photos @@ -140,10 +142,10 @@ Sometimes, you have a resource that clients always look up without referencing a get 'profile', to: 'users#show' ``` -Passing a `String` to `get` will expect a `controller#action` format, while passing a `Symbol` will map directly to an action: +Passing a `String` to `get` will expect a `controller#action` format, while passing a `Symbol` will map directly to an action but you must also specify the `controller:` to use: ```ruby -get 'profile', to: :show +get 'profile', to: :show, controller: 'users' ``` This resourceful route: @@ -250,11 +252,11 @@ TIP: _If you need to use a different controller namespace inside a `namespace` b It's common to have resources that are logically children of other resources. For example, suppose your application includes these models: ```ruby -class Magazine < ActiveRecord::Base +class Magazine < ApplicationRecord has_many :ads end -class Ad < ActiveRecord::Base +class Ad < ApplicationRecord belongs_to :magazine end ``` @@ -390,7 +392,7 @@ The comments resource here will have the following routes generated for it: ### Routing concerns -Routing Concerns allows you to declare common routes that can be reused inside other resources and routes. To define a concern: +Routing concerns allow you to declare common routes that can be reused inside other resources and routes. To define a concern: ```ruby concern :commentable do @@ -615,6 +617,8 @@ get 'photos/:id', to: 'photos#show', defaults: { format: 'jpg' } Rails would match `photos/12` to the `show` action of `PhotosController`, and set `params[:format]` to `"jpg"`. +NOTE: You cannot override defaults via query parameters - this is for security reasons. The only defaults that can be overridden are dynamic segments via substitution in the URL path. + ### Naming Routes You can specify a name for any route using the `:as` option: @@ -702,6 +706,8 @@ end NOTE: Request constraints work by calling a method on the [Request object](action_controller_overview.html#the-request-object) with the same name as the hash key and then compare the return value with the hash value. Therefore, constraint values should match the corresponding Request object method return type. For example: `constraints: { subdomain: 'api' }` will match an `api` subdomain as expected, however using a symbol `constraints: { subdomain: :api }` will not, because `request.subdomain` returns `'api'` as a String. +NOTE: There is an exception for the `format` constraint: while it's a method on the Request object, it's also an implicit optional parameter on every path. Segment constraints take precedence and the `format` constraint is only applied as such when enforced through a hash. For example, `get 'foo', constraints: { format: 'json' }` will match `GET /foo` because the format is optional by default. However, you can [use a lambda](#advanced-constraints) like in `get 'foo', constraints: lambda { |req| req.format == :json }` and the route will only match explicit JSON requests. + ### Advanced Constraints If you have a more advanced constraint, you can provide an object that responds to `matches?` that Rails should use. Let's say you wanted to route all users on a blacklist to the `BlacklistController`. You could do: @@ -793,7 +799,11 @@ get '/stories/:name', to: redirect { |path_params, req| "/articles/#{path_params get '/stories', to: redirect { |path_params, req| "/articles/#{req.subdomain}" } ``` -Please note that this redirection is a 301 "Moved Permanently" redirect. Keep in mind that some web browsers or proxy servers will cache this type of redirect, making the old page inaccessible. +Please note that default redirection is a 301 "Moved Permanently" redirect. Keep in mind that some web browsers or proxy servers will cache this type of redirect, making the old page inaccessible. You can use the `:status` option to change the response status: + +```ruby +get '/stories/:name', to: redirect('/articles/%{name}', status: 302) +``` In all of these cases, if you don't provide the leading host (`http://www.example.com`), Rails will take those details from the current request. @@ -1087,6 +1097,20 @@ edit_videos GET /videos/:identifier/edit(.:format) videos#edit Video.find_by(identifier: params[:identifier]) ``` +You can override `ActiveRecord::Base#to_param` of a related model to construct +a URL: + +```ruby +class Video < ApplicationRecord + def to_param + identifier + end +end + +video = Video.find_by(identifier: "Roman-Holiday") +edit_videos_path(video) # => "/videos/Roman-Holiday" +``` + Inspecting and Testing Routes ----------------------------- @@ -1094,16 +1118,16 @@ Rails offers facilities for inspecting and testing your routes. ### Listing Existing Routes -To get a complete list of the available routes in your application, visit `http://localhost:3000/rails/info/routes` in your browser while your server is running in the **development** environment. You can also execute the `rake routes` command in your terminal to produce the same output. +To get a complete list of the available routes in your application, visit `http://localhost:3000/rails/info/routes` in your browser while your server is running in the **development** environment. You can also execute the `rails routes` command in your terminal to produce the same output. -Both methods will list all of your routes, in the same order that they appear in `routes.rb`. For each route, you'll see: +Both methods will list all of your routes, in the same order that they appear in `config/routes.rb`. For each route, you'll see: * The route name (if any) * The HTTP verb used (if the route doesn't respond to all verbs) * The URL pattern to match * The routing parameters for the route -For example, here's a small section of the `rake routes` output for a RESTful route: +For example, here's a small section of the `rails routes` output for a RESTful route: ``` users GET /users(.:format) users#index @@ -1112,13 +1136,24 @@ For example, here's a small section of the `rake routes` output for a RESTful ro edit_user GET /users/:id/edit(.:format) users#edit ``` -You may restrict the listing to the routes that map to a particular controller setting the `CONTROLLER` environment variable: +You can search through your routes with the --grep option (-g for short). This outputs any routes that partially match the URL helper method name, the HTTP verb, or the URL path. + +``` +$ bin/rake routes --grep new_comment +$ bin/rake routes -g POST +$ bin/rake routes -g admin +``` + +If you only want to see the routes that map to a specific controller, there's the --controller option (-c for short). -```bash -$ CONTROLLER=users bin/rake routes +``` +$ bin/rake routes --controller users +$ bin/rake routes --controller admin/users +$ bin/rake routes -c Comments +$ bin/rake routes -c Articles::CommentsController ``` -TIP: You'll find that the output from `rake routes` is much more readable if you widen your terminal window until the output lines don't wrap. +TIP: You'll find that the output from `rails routes` is much more readable if you widen your terminal window until the output lines don't wrap. ### Testing Routes diff --git a/guides/source/security.md b/guides/source/security.md index 485b108d12..96b9f4bcce 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -93,9 +93,16 @@ Rails 2 introduced a new default session storage, CookieStore. CookieStore saves * Cookies imply a strict size limit of 4kB. This is fine as you should not store large amounts of data in a session anyway, as described before. _Storing the current user's database id in a session is usually ok_. -* The client can see everything you store in a session, because it is stored in clear-text (actually Base64-encoded, so not encrypted). So, of course, _you don't want to store any secrets here_. To prevent session hash tampering, a digest is calculated from the session with a server-side secret and inserted into the end of the cookie. +* The client can see everything you store in a session, because it is stored in clear-text (actually Base64-encoded, so not encrypted). So, of course, _you don't want to store any secrets here_. To prevent session hash tampering, a digest is calculated from the session with a server-side secret (`secrets.secret_token`) and inserted into the end of the cookie. -That means the security of this storage depends on this secret (and on the digest algorithm, which defaults to SHA1, for compatibility). So _don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters_. +However, since Rails 4, the default store is EncryptedCookieStore. With +EncryptedCookieStore the session is encrypted before being stored in a cookie. +This prevents the user from accessing and tampering the content of the cookie. +Thus the session becomes a more secure place to store data. The encryption is +done using a server-side secret key `secrets.secret_key_base` stored in +`config/secrets.yml`. + +That means the security of this storage depends on this secret (and on the digest algorithm, which defaults to SHA1, for compatibility). So _don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters, use `rails secret` instead_. `secrets.secret_key_base` is used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get `secrets.secret_key_base` initialized to a random key present in `config/secrets.yml`, e.g.: @@ -164,7 +171,7 @@ NOTE: _Sessions that never expire extend the time-frame for attacks such as cros One possibility is to set the expiry time-stamp of the cookie with the session id. However the client can edit cookies that are stored in the web browser so expiring sessions on the server is safer. Here is an example of how to _expire sessions in a database table_. Call `Session.sweep("20 minutes")` to expire sessions that were used longer than 20 minutes ago. ```ruby -class Session < ActiveRecord::Base +class Session < ApplicationRecord def self.sweep(time = 1.hour) if time.is_a?(String) time = time.split.inject { |count, unit| count.to_i.send(unit) } @@ -189,13 +196,12 @@ This attack method works by including malicious code or a link in a page that ac  -In the [session chapter](#sessions) you have learned that most Rails applications use cookie-based sessions. Either they store the session id in the cookie and have a server-side session hash, or the entire session hash is on the client-side. In either case the browser will automatically send along the cookie on every request to a domain, if it can find a cookie for that domain. The controversial point is, that it will also send the cookie, if the request comes from a site of a different domain. Let's start with an example: +In the [session chapter](#sessions) you have learned that most Rails applications use cookie-based sessions. Either they store the session id in the cookie and have a server-side session hash, or the entire session hash is on the client-side. In either case the browser will automatically send along the cookie on every request to a domain, if it can find a cookie for that domain. The controversial point is that if the request comes from a site of a different domain, it will also send the cookie. Let's start with an example: -* Bob browses a message board and views a post from a hacker where there is a crafted HTML image element. The element references a command in Bob's project management application, rather than an image file. -* `<img src="http://www.webapp.com/project/1/destroy">` -* Bob's session at www.webapp.com is still alive, because he didn't log out a few minutes ago. -* By viewing the post, the browser finds an image tag. It tries to load the suspected image from www.webapp.com. As explained before, it will also send along the cookie with the valid session id. -* The web application at www.webapp.com verifies the user information in the corresponding session hash and destroys the project with the ID 1. It then returns a result page which is an unexpected result for the browser, so it will not display the image. +* Bob browses a message board and views a post from a hacker where there is a crafted HTML image element. The element references a command in Bob's project management application, rather than an image file: `<img src="http://www.webapp.com/project/1/destroy">` +* Bob's session at `www.webapp.com` is still alive, because he didn't log out a few minutes ago. +* By viewing the post, the browser finds an image tag. It tries to load the suspected image from `www.webapp.com`. As explained before, it will also send along the cookie with the valid session id. +* The web application at `www.webapp.com` verifies the user information in the corresponding session hash and destroys the project with the ID 1. It then returns a result page which is an unexpected result for the browser, so it will not display the image. * Bob doesn't notice the attack - but a few days later he finds out that project number one is gone. It is important to notice that the actual crafted image or link doesn't necessarily have to be situated in the web application's domain, it can be anywhere - in a forum, blog post or email. @@ -218,9 +224,9 @@ The HTTP protocol basically provides two main types of requests - GET and POST ( * The interaction _changes the state_ of the resource in a way that the user would perceive (e.g., a subscription to a service), or * The user is _held accountable for the results_ of the interaction. -If your web application is RESTful, you might be used to additional HTTP verbs, such as PATCH, PUT or DELETE. Most of today's web browsers, however do not support them - only GET and POST. Rails uses a hidden `_method` field to handle this barrier. +If your web application is RESTful, you might be used to additional HTTP verbs, such as PATCH, PUT or DELETE. Most of today's web browsers, however, do not support them - only GET and POST. Rails uses a hidden `_method` field to handle this barrier. -_POST requests can be sent automatically, too_. Here is an example for a link which displays www.harmless.com as destination in the browser's status bar. In fact it dynamically creates a new form that sends a POST request. +_POST requests can be sent automatically, too_. In this example, the link www.harmless.com is shown as the destination in the browser's status bar. But it has actually dynamically created a new form that sends a POST request. ```html <a href="http://www.harmless.com/" onclick=" @@ -239,7 +245,9 @@ Or the attacker places the code into the onmouseover event handler of an image: <img src="http://www.harmless.com/img" width="400" height="400" onmouseover="..." /> ``` -There are many other possibilities, like using a `<script>` tag to make a cross-site request to a URL with a JSONP or JavaScript response. The response is executable code that the attacker can find a way to run, possibly extracting sensitive data. To protect against this data leakage, we disallow cross-site `<script>` tags. Only Ajax requests may have JavaScript responses since `XMLHttpRequest` is subject to the browser Same-Origin policy - meaning only your site can initiate the request. +There are many other possibilities, like using a `<script>` tag to make a cross-site request to a URL with a JSONP or JavaScript response. The response is executable code that the attacker can find a way to run, possibly extracting sensitive data. To protect against this data leakage, we must disallow cross-site `<script>` tags. Ajax requests, however, obey the browser's same-origin policy (only your own site is allowed to initiate `XmlHttpRequest`) so we can safely allow them to return JavaScript responses. + +Note: We can't distinguish a `<script>` tag's origin—whether it's a tag on your own site or on some other malicious site—so we must block all `<script>` across the board, even if it's actually a safe same-origin script served from your own site. In these cases, explicitly skip CSRF protection on actions that serve JavaScript meant for a `<script>` tag. To protect against all other forged requests, we introduce a _required security token_ that our site knows but other sites don't know. We include the security token in requests and verify it on the server. This is a one-liner in your application controller, and is the default for newly created rails applications: @@ -293,7 +301,7 @@ This will redirect the user to the main action if they tried to access a legacy http://www.example.com/site/legacy?param1=xy¶m2=23&host=www.attacker.com ``` -If it is at the end of the URL it will hardly be noticed and redirects the user to the attacker.com host. A simple countermeasure would be to _include only the expected parameters in a legacy action_ (again a whitelist approach, as opposed to removing unexpected parameters). _And if you redirect to an URL, check it with a whitelist or a regular expression_. +If it is at the end of the URL it will hardly be noticed and redirects the user to the attacker.com host. A simple countermeasure would be to _include only the expected parameters in a legacy action_ (again a whitelist approach, as opposed to removing unexpected parameters). _And if you redirect to a URL, check it with a whitelist or a regular expression_. #### Self-contained XSS @@ -373,9 +381,9 @@ Refer to the Injection section for countermeasures against XSS. It is _recommend **CSRF** Cross-Site Request Forgery (CSRF), also known as Cross-Site Reference Forgery (XSRF), is a gigantic attack method, it allows the attacker to do everything the administrator or Intranet user may do. As you have already seen above how CSRF works, here are a few examples of what attackers can do in the Intranet or admin interface. -A real-world example is a [router reconfiguration by CSRF](http://www.h-online.com/security/news/item/Symantec-reports-first-active-attack-on-a-DSL-router-735883.html). The attackers sent a malicious e-mail, with CSRF in it, to Mexican users. The e-mail claimed there was an e-card waiting for them, but it also contained an image tag that resulted in a HTTP-GET request to reconfigure the user's router (which is a popular model in Mexico). The request changed the DNS-settings so that requests to a Mexico-based banking site would be mapped to the attacker's site. Everyone who accessed the banking site through that router saw the attacker's fake web site and had their credentials stolen. +A real-world example is a [router reconfiguration by CSRF](http://www.h-online.com/security/news/item/Symantec-reports-first-active-attack-on-a-DSL-router-735883.html). The attackers sent a malicious e-mail, with CSRF in it, to Mexican users. The e-mail claimed there was an e-card waiting for the user, but it also contained an image tag that resulted in a HTTP-GET request to reconfigure the user's router (which is a popular model in Mexico). The request changed the DNS-settings so that requests to a Mexico-based banking site would be mapped to the attacker's site. Everyone who accessed the banking site through that router saw the attacker's fake web site and had their credentials stolen. -Another example changed Google Adsense's e-mail address and password by. If the victim was logged into Google Adsense, the administration interface for Google advertisements campaigns, an attacker could change their credentials.
+Another example changed Google Adsense's e-mail address and password. If the victim was logged into Google Adsense, the administration interface for Google advertisement campaigns, an attacker could change the credentials of the victim.
Another popular attack is to spam your web application, your blog or forum to propagate malicious XSS. Of course, the attacker has to know the URL structure, but most Rails URLs are quite straightforward or they will be easy to find out, if it is an open-source application's admin interface. The attacker may even do 1,000 lucky guesses by just including malicious IMG-tags which try every possible combination. @@ -398,7 +406,7 @@ NOTE: _Almost every web application has to deal with authorization and authentic There are a number of authentication plug-ins for Rails available. Good ones, such as the popular [devise](https://github.com/plataformatec/devise) and [authlogic](https://github.com/binarylogic/authlogic), store only encrypted passwords, not plain-text passwords. In Rails 3.1 you can use the built-in `has_secure_password` method which has similar features. -Every new user gets an activation code to activate their account when they get an e-mail with a link in it. After activating the account, the activation_code columns will be set to NULL in the database. If someone requested an URL like these, they would be logged in as the first activated user found in the database (and chances are that this is the administrator): +Every new user gets an activation code to activate their account when they get an e-mail with a link in it. After activating the account, the activation_code columns will be set to NULL in the database. If someone requested a URL like these, they would be logged in as the first activated user found in the database (and chances are that this is the administrator): ``` http://localhost:3006/user/activate @@ -754,7 +762,7 @@ s = sanitize(user_input, tags: tags, attributes: %w(href title)) This allows only the given tags and does a good job, even against all kinds of tricks and malformed tags. -As a second step, _it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input-filtered (as in the search form example earlier on). _Use `escapeHTML()` (or its alias `h()`) method_ to replace the HTML input characters &, ", <, > by their uninterpreted representations in HTML (`&`, `"`, `<`;, and `>`). However, it can easily happen that the programmer forgets to use it, so _it is recommended to use the SafeErb gem. SafeErb reminds you to escape strings from external sources. +As a second step, _it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input-filtered (as in the search form example earlier on). _Use `escapeHTML()` (or its alias `h()`) method_ to replace the HTML input characters &, ", <, and > by their uninterpreted representations in HTML (`&`, `"`, `<`, and `>`). However, it can easily happen that the programmer forgets to use it, so _it is recommended to use the SafeErb gem. SafeErb reminds you to escape strings from external sources. ##### Obfuscation and Encoding Injection @@ -785,15 +793,13 @@ Another proof-of-concept webmail worm is Nduja, a cross-domain worm for four Ita In December 2006, 34,000 actual user names and passwords were stolen in a [MySpace phishing attack](http://news.netcraft.com/archives/2006/10/27/myspace_accounts_compromised_by_phishers.html). The idea of the attack was to create a profile page named "login_home_index_html", so the URL looked very convincing. Specially-crafted HTML and CSS was used to hide the genuine MySpace content from the page and instead display its own login form. -The MySpace Samy worm will be discussed in the CSS Injection section. - ### CSS Injection INFO: _CSS Injection is actually JavaScript injection, because some browsers (IE, some versions of Safari and others) allow JavaScript in CSS. Think twice about allowing custom CSS in your web application._ -CSS Injection is explained best by a well-known worm, the [MySpace Samy worm](http://namb.la/popular/tech.html). This worm automatically sent a friend request to Samy (the attacker) simply by visiting his profile. Within several hours he had over 1 million friend requests, but it creates too much traffic on MySpace, so that the site goes offline. The following is a technical explanation of the worm. +CSS Injection is explained best by the well-known [MySpace Samy worm](http://namb.la/popular/tech.html). This worm automatically sent a friend request to Samy (the attacker) simply by visiting his profile. Within several hours he had over 1 million friend requests, which created so much traffic that MySpace went offline. The following is a technical explanation of that worm. -MySpace blocks many tags, however it allows CSS. So the worm's author put JavaScript into CSS like this: +MySpace blocked many tags, but allowed CSS. So the worm's author put JavaScript into CSS like this: ```html <div style="background:url('javascript:alert(1)')"> @@ -817,7 +823,7 @@ The next problem was MySpace filtering the word "javascript", so the author used <div id="mycode" expr="alert('hah!')" style="background:url('java↵
script:eval(document.all.mycode.expr)')"> ``` -Another problem for the worm's author were CSRF security tokens. Without them he couldn't send a friend request over POST. He got around it by sending a GET to the page right before adding a user and parsing the result for the CSRF token. +Another problem for the worm's author was the [CSRF security tokens](#cross-site-request-forgery-csrf). Without them he couldn't send a friend request over POST. He got around it by sending a GET to the page right before adding a user and parsing the result for the CSRF token. In the end, he got a 4 KB worm, which he injected into his profile page. @@ -1008,18 +1014,12 @@ config.action_dispatch.default_headers.clear Here is a list of common headers: -* X-Frame-Options -_'SAMEORIGIN' in Rails by default_ - allow framing on same domain. Set it to 'DENY' to deny framing at all or 'ALLOWALL' if you want to allow framing for all website. -* X-XSS-Protection -_'1; mode=block' in Rails by default_ - use XSS Auditor and block page if XSS attack is detected. Set it to '0;' if you want to switch XSS Auditor off(useful if response contents scripts from request parameters) -* X-Content-Type-Options -_'nosniff' in Rails by default_ - stops the browser from guessing the MIME type of a file. -* X-Content-Security-Policy -[A powerful mechanism for controlling which sites certain content types can be loaded from](http://w3c.github.io/webappsec/specs/content-security-policy/csp-specification.dev.html) -* Access-Control-Allow-Origin -Used to control which sites are allowed to bypass same origin policies and send cross-origin requests. -* Strict-Transport-Security -[Used to control if the browser is allowed to only access a site over a secure connection](http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security) +* **X-Frame-Options:** _'SAMEORIGIN' in Rails by default_ - allow framing on same domain. Set it to 'DENY' to deny framing at all or 'ALLOWALL' if you want to allow framing for all website. +* **X-XSS-Protection:** _'1; mode=block' in Rails by default_ - use XSS Auditor and block page if XSS attack is detected. Set it to '0;' if you want to switch XSS Auditor off(useful if response contents scripts from request parameters) +* **X-Content-Type-Options:** _'nosniff' in Rails by default_ - stops the browser from guessing the MIME type of a file. +* **X-Content-Security-Policy:** [A powerful mechanism for controlling which sites certain content types can be loaded from](http://w3c.github.io/webappsec/specs/content-security-policy/csp-specification.dev.html) +* **Access-Control-Allow-Origin:** Used to control which sites are allowed to bypass same origin policies and send cross-origin requests. +* **Strict-Transport-Security:** [Used to control if the browser is allowed to only access a site over a secure connection](http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security) Environmental Security ---------------------- @@ -1046,7 +1046,7 @@ If you want an exception to be raised when some key is blank, use the bang version: ```ruby -Rails.application.secrets.some_api_key! # => raises KeyError +Rails.application.secrets.some_api_key! # => raises KeyError: key not found: :some_api_key ``` Additional Resources @@ -1057,4 +1057,3 @@ The security landscape shifts and it is important to keep up to date, because mi * Subscribe to the Rails security [mailing list](http://groups.google.com/group/rubyonrails-security) * [Keep up to date on the other application layers](http://secunia.com/) (they have a weekly newsletter, too) * A [good security blog](https://www.owasp.org) including the [Cross-Site scripting Cheat Sheet](https://www.owasp.org/index.php/DOM_based_XSS_Prevention_Cheat_Sheet) - diff --git a/guides/source/testing.md b/guides/source/testing.md index f40e765242..f4894d4c11 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -25,15 +25,7 @@ Rails tests can also simulate browser requests and thus you can test your applic 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. - -### The Test Environment - -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`. - -A dedicated test database allows you to set up and interact with test data in isolation. This way your tests can mangle test data with confidence, without worrying about the data in the development or production databases. - -Also, each environment's configuration can be modified similarly. In this case, we can modify our test environment by changing the options found in `config/environments/test.rb`. +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. ### Rails Sets up for Testing from the Word Go @@ -51,126 +43,23 @@ Fixtures are a way of organizing test data; they reside in the `fixtures` direct The `test_helper.rb` file holds the default configuration for your tests. -### 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. -You can find comprehensive documentation in the [Fixtures API documentation](http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html). - -#### 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 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, Rails automatically creates fixture stubs in this directory. - -#### YAML - -YAML-formatted fixtures are a human-friendly way to describe your sample data. These types of fixtures have the **.yml** file extension (as in `users.yml`). - -Here's a sample YAML fixture file: - -```yaml -# lo & behold! I am a YAML comment! -david: - name: David Heinemeier Hansson - birthday: 1979-10-15 - profession: Systems development - -steve: - name: Steve Ross Kellock - birthday: 1974-09-27 - profession: guy with keyboard -``` - -Each fixture is given a name followed by an indented list of colon-separated key/value pairs. Records are typically separated by a blank line. You can place comments in a fixture file by using the # character in the first column. - -If you are working with [associations](/association_basics.html), you can simply -define a reference node between two different fixtures. Here's an example with -a `belongs_to`/`has_many` association: - -```yaml -# In fixtures/categories.yml -about: - name: About - -# In fixtures/articles.yml -one: - title: Welcome to Rails! - body: Hello world! - category: about -``` - -Notice the `category` key of the `one` article found in `fixtures/articles.yml` has a value of `about`. This tells Rails to load the category `about` found in `fixtures/categories.yml`. - -NOTE: For associations to reference one another by name, you cannot specify the `id:` attribute on the associated fixtures. Rails will auto assign a primary key to be consistent between runs. For more information on this association behavior please read the [Fixtures API documentation](http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html). - -#### ERB'in It Up - -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#{n}" %> - email: <%= "user#{n}@example.com" %> -<% end %> -``` - -#### Fixtures in Action - -Rails by default automatically loads all fixtures from the `test/fixtures` directory for your models and controllers test. Loading involves three steps: - -1. Remove any existing data from the table corresponding to the fixture -2. Load the fixture data into the table -3. Dump the fixture data into a method in case you want to access it directly - -TIP: In order to remove existing data from the database, Rails tries to disable referential integrity triggers (like foreign keys and check constraints). If you are getting annoying permission errors on running tests, make sure the database user has privilege to disable these triggers in testing environment. (In PostgreSQL, only superusers can disable all triggers. Read more about PostgreSQL permissions [here](http://blog.endpoint.com/2012/10/postgres-system-triggers-error.html)) - -#### Fixtures are Active Record objects - -Fixtures are instances of Active Record. As mentioned in point #3 above, you can access the object directly because it is automatically available as a method whose scope is local of the test case. For example: - -```ruby -# this will return the User object for the fixture named david -users(:david) -# this will return the property for david called id -users(:david).id +### The Test Environment -# one can also access methods available on the User class -email(david.partner.email, david.location_tonight) -``` +By default, every Rails application has three environments: development, test, and production. -To get multiple fixtures at once, you can pass in a list of fixture names. For example: +Each environment's configuration can be modified similarly. In this case, we can modify our test environment by changing the options found in `config/environments/test.rb`. -```ruby -# this will return an array containing the fixtures david and steve -users(:david, :steve) -``` +NOTE: Your tests are run under `RAILS_ENV=test`. -### Console Tasks for Running your Tests +### Rails meets Minitest -Rails comes with a CLI command to run tests. -Here are some examples of how to use it: +If you remember, we used the `rails generate model` command in the +[Getting Started with Rails](getting_started.html) guide. We created our first +model, and among other things it created test stubs in the `test` directory: ```bash -$ bin/rails test # run all tests in the `test` directory -$ bin/rails test test/controllers # run all tests from specific directory -$ bin/rails test test/models/post_test.rb # run specific test -$ bin/rails test test/models/post_test.rb:44 # run specific test and line -``` - -We will cover each of types Rails tests listed above in this guide. - -Model Testing ------------------------- - -For this guide we will be using the application we built in the [Getting Started with Rails](getting_started.html) guide. - -If you remember when you used the `rails generate scaffold` command from earlier. We created our first resource among other things it created a test stub in the `test/models` directory: - -```bash -$ bin/rails generate scaffold article title:string body:text +$ bin/rails generate model article title:string body:text ... create app/models/article.rb create test/models/article_test.rb @@ -178,14 +67,6 @@ create test/fixtures/articles.yml ... ``` -You can also generate the test stub for a model using the following command: - -```bash -$ bin/rails generate test_unit:model article title:string body:text -create test/models/article_test.rb -create test/fixtures/articles.yml -``` - The default test stub in `test/models/article_test.rb` looks like this: ```ruby @@ -204,13 +85,13 @@ A line by line examination of this file will help get you oriented to Rails test require 'test_helper' ``` -By requiring this file, `test_helper.rb` the default configuration to run our tests is loaded. We will include this with all the tests we write, so any methods added to this file are available to all your tests. +By requiring this file, `test_helper.rb` the default configuration to run our tests is loaded. We will include this with all the tests we write, so any methods added to this file are available to all our tests. ```ruby class ArticleTest < ActiveSupport::TestCase ``` -The `ArticleTest` class defines a _test case_ because it inherits from `ActiveSupport::TestCase`. `ArticleTest` thus has all the methods available from `ActiveSupport::TestCase`. Later in this guide, you'll see some of the methods it gives you. +The `ArticleTest` class defines a _test case_ because it inherits from `ActiveSupport::TestCase`. `ArticleTest` thus has all the methods available from `ActiveSupport::TestCase`. Later in this guide, we'll see some of the methods it gives us. Any method defined within a class inherited from `Minitest::Test` (which is the superclass of `ActiveSupport::TestCase`) that begins with `test_` (case sensitive) is simply called a test. So, methods defined as `test_password` and `test_valid_password` are legal test names and are run automatically when the test case is run. @@ -248,48 +129,7 @@ An assertion is a line of code that evaluates an object (or expression) for expe * does this line of code throw an exception? * is the user's password greater than 5 characters? -Every test must contain at least one assertion, with no restriction as to how many assertions are allowed. Only when all the assertions are successful will the test pass. - -### Maintaining the test database schema - -In order to run your tests, your test database will need to have the current -structure. The test helper checks whether your test database has any pending -migrations. If so, it will try to load your `db/schema.rb` or `db/structure.sql` -into the test database. If migrations are still pending, an error will be -raised. Usually this indicates that your schema is not fully migrated. Running -the migrations against the development database (`bin/rake db:migrate`) will -bring the schema up to date. - -NOTE: If existing migrations required modifications, the test database needs to -be rebuilt. This can be done by executing `bin/rake db:test:prepare`. - -### Running Tests - -Running a test is as simple as invoking the file containing the test cases through `rails test` command. - -```bash -$ bin/rails test test/models/article_test.rb -. - -Finished tests in 0.009262s, 107.9680 tests/s, 107.9680 assertions/s. - -1 tests, 1 assertions, 0 failures, 0 errors, 0 skips -``` - -This will run all test methods from the test case. - -You can also run a particular test method from the test case by running the test and providing the `test method name`. - -```bash -$ bin/rails test test/models/article_test.rb test_the_truth -. - -Finished tests in 0.009064s, 110.3266 tests/s, 110.3266 assertions/s. - -1 tests, 1 assertions, 0 failures, 0 errors, 0 skips -``` - -The `.` (dot) above indicates a passing test. When a test fails you see an `F`; when a test throws an error you see an `E` in its place. The last line of the output is the summary. +Every test may contain one or more assertions, with no restriction as to how many assertions are allowed. Only when all the assertions are successful will the test pass. #### Your first failing test @@ -317,7 +157,7 @@ Failed assertion, no message given. 1 tests, 1 assertions, 1 failures, 0 errors, 0 skips ``` -In the output, `F` denotes a failure. You can see the corresponding trace shown under `1)` along with the name of the failing test. The next few lines contain the stack trace followed by a message which mentions the actual value and the expected value by the assertion. The default assertion messages provide just enough information to help pinpoint the error. To make the assertion failure message more readable, every assertion provides an optional message parameter, as shown here: +In the output, `F` denotes a failure. You can see the corresponding trace shown under `1)` along with the name of the failing test. The next few lines contain the stack trace followed by a message that mentions the actual value and the expected value by the assertion. The default assertion messages provide just enough information to help pinpoint the error. To make the assertion failure message more readable, every assertion provides an optional message parameter, as shown here: ```ruby test "should not save article without title" do @@ -337,7 +177,7 @@ Saved the article without a title Now to get this test to pass we can add a model level validation for the _title_ field. ```ruby -class Article < ActiveRecord::Base +class Article < ApplicationRecord validates :title, presence: true end ``` @@ -425,7 +265,7 @@ By now you've caught a glimpse of some of the assertions that are available. Ass Here's an extract of the assertions you can use with [`Minitest`](https://github.com/seattlerb/minitest), the default testing library used by Rails. The `[msg]` parameter is an optional string message you can -specify to make your test failure messages clearer. It's not required. +specify to make your test failure messages clearer. | Assertion | Purpose | | ---------------------------------------------------------------- | ------- | @@ -443,8 +283,8 @@ specify to make your test failure messages clearer. It's not required. | `assert_no_match( regexp, string, [msg] )` | Ensures that a string doesn't match the regular expression.| | `assert_includes( collection, obj, [msg] )` | Ensures that `obj` is in `collection`.| | `assert_not_includes( collection, obj, [msg] )` | Ensures that `obj` is not in `collection`.| -| `assert_in_delta( expecting, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are within `delta` of each other.| -| `assert_not_in_delta( expecting, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are not within `delta` of each other.| +| `assert_in_delta( expected, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are within `delta` of each other.| +| `assert_not_in_delta( expected, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are not within `delta` of each other.| | `assert_throws( symbol, [msg] ) { block }` | Ensures that the given block throws the symbol.| | `assert_raises( exception1, exception2, ... ) { block }` | Ensures that the given block raises one of the given exceptions.| | `assert_nothing_raised( exception1, exception2, ... ) { block }` | Ensures that the given block doesn't raise one of the given exceptions.| @@ -458,13 +298,13 @@ specify to make your test failure messages clearer. It's not required. | `assert_not_operator( obj1, operator, [obj2], [msg] )` | Ensures that `obj1.operator(obj2)` is false.| | `assert_predicate ( obj, predicate, [msg] )` | Ensures that `obj.predicate` is true, e.g. `assert_predicate str, :empty?`| | `assert_not_predicate ( obj, predicate, [msg] )` | Ensures that `obj.predicate` is false, e.g. `assert_not_predicate str, :empty?`| -| `assert_send( array, [msg] )` | Ensures that executing the method listed in `array[1]` on the object in `array[0]` with the parameters of `array[2 and up]` is true. This one is weird eh?| +| `assert_send( array, [msg] )` | Ensures that executing the method listed in `array[1]` on the object in `array[0]` with the parameters of `array[2 and up]` is true, e.g. assert_send [@user, :full_name, 'Sam Smith']. This one is weird eh?| | `flunk( [msg] )` | Ensures failure. This is useful to explicitly mark a test that isn't finished yet.| The above are a subset of assertions that minitest supports. For an exhaustive & more up-to-date list, please check [Minitest API documentation](http://docs.seattlerb.org/minitest/), specifically -[`Minitest::Assertions`](http://docs.seattlerb.org/minitest/Minitest/Assertions.html) +[`Minitest::Assertions`](http://docs.seattlerb.org/minitest/Minitest/Assertions.html). Because of the modular nature of the testing framework, it is possible to create your own assertions. In fact, that's exactly what Rails does. It includes some specialized assertions to make your life easier. @@ -481,16 +321,15 @@ Rails adds some custom assertions of its own to the `minitest` framework: | `assert_recognizes(expected_options, path, extras={}, message=nil)` | Asserts that the routing of the given path was handled correctly and that the parsed options (given in the expected_options hash) match path. Basically, it asserts that Rails recognizes the route given by expected_options.| | `assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)` | Asserts that the provided options can be used to generate the provided path. This is the inverse of assert_recognizes. The extras parameter is used to tell the request the names and values of additional request parameters that would be in a query string. The message parameter allows you to specify a custom error message for assertion failures.| | `assert_response(type, message = nil)` | Asserts that the response comes with a specific status code. You can specify `:success` to indicate 200-299, `:redirect` to indicate 300-399, `:missing` to indicate 404, or `:error` to match the 500-599 range. You can also pass an explicit status number or its symbolic equivalent. For more information, see [full list of status codes](http://rubydoc.info/github/rack/rack/master/Rack/Utils#HTTP_STATUS_CODES-constant) and how their [mapping](http://rubydoc.info/github/rack/rack/master/Rack/Utils#SYMBOL_TO_STATUS_CODE-constant) works.| -| `assert_redirected_to(options = {}, message=nil)` | Assert that the redirection options passed in match those of the redirect called in the latest action. This match can be partial, such that `assert_redirected_to(controller: "weblog")` will also match the redirection of `redirect_to(controller: "weblog", action: "show")` and so on. You can also pass named routes such as `assert_redirected_to root_path` and Active Record objects such as `assert_redirected_to @article`.| +| `assert_redirected_to(options = {}, message=nil)` | Asserts that the redirection options passed in match those of the redirect called in the latest action. This match can be partial, such that `assert_redirected_to(controller: "weblog")` will also match the redirection of `redirect_to(controller: "weblog", action: "show")` and so on. You can also pass named routes such as `assert_redirected_to root_path` and Active Record objects such as `assert_redirected_to @article`.| You'll see the usage of some of these assertions in the next chapter. -### A Brief Note About Minitest +### A Brief Note About Test Cases All the basic assertions such as `assert_equal` defined in `Minitest::Assertions` are also available in the classes we use in our own test cases. In fact, Rails provides the following classes for you to inherit from: * `ActiveSupport::TestCase` -* `ActionController::TestCase` * `ActionMailer::TestCase` * `ActionView::TestCase` * `ActionDispatch::IntegrationTest` @@ -498,14 +337,310 @@ All the basic assertions such as `assert_equal` defined in `Minitest::Assertions Each of these classes include `Minitest::Assertions`, allowing us to use all of the basic assertions in our tests. -NOTE: For more information on `Minitest`, refer to [Minitest](http://docs.seattlerb.org/minitest) +NOTE: For more information on `Minitest`, refer to [its +documentation](http://docs.seattlerb.org/minitest). + +### The Rails Test Runner + +We can run all of our tests at once by using the `rails test` command. + +Or we can run a single test by passing the `rails test` command the filename containing the test cases. + +```bash +$ bin/rails test test/models/article_test.rb +. + +Finished tests in 0.009262s, 107.9680 tests/s, 107.9680 assertions/s. + +1 tests, 1 assertions, 0 failures, 0 errors, 0 skips +``` + +This will run all test methods from the test case. + +You can also run a particular test method from the test case by providing the +`-n` or `--name` flag and the test's method name. + +```bash +$ bin/rails test test/models/article_test.rb -n test_the_truth +. + +Finished tests in 0.009064s, 110.3266 tests/s, 110.3266 assertions/s. + +1 tests, 1 assertions, 0 failures, 0 errors, 0 skips +``` + +You can also run a test at a specific line by providing the line number. + +```bash +$ bin/rails test test/models/post_test.rb:44 # run specific test and line +``` + +You can also run an entire directory of tests by providing the path to the directory. + +```bash +$ bin/rails test test/controllers # run all tests from specific directory +``` + + +The Test Database +----------------- + +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. + +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`. + +A dedicated test database allows you to set up and interact with test data in isolation. This way your tests can mangle test data with confidence, without worrying about the data in the development or production databases. + + +### Maintaining the test database schema + +In order to run your tests, your test database will need to have the current +structure. The test helper checks whether your test database has any pending +migrations. It will try to load your `db/schema.rb` or `db/structure.sql` +into the test database. If migrations are still pending, an error will be +raised. Usually this indicates that your schema is not fully migrated. Running +the migrations against the development database (`bin/rails db:migrate`) will +bring the schema up to date. + +NOTE: If there were modifications to existing migrations, the test database needs to +be rebuilt. This can be done by executing `bin/rails db:test:prepare`. + +### 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. +You can find comprehensive documentation in the [Fixtures API documentation](http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html). + +#### 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 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, Rails automatically creates fixture stubs in this directory. + +#### YAML + +YAML-formatted fixtures are a human-friendly way to describe your sample data. These types of fixtures have the **.yml** file extension (as in `users.yml`). + +Here's a sample YAML fixture file: + +```yaml +# lo & behold! I am a YAML comment! +david: + name: David Heinemeier Hansson + birthday: 1979-10-15 + profession: Systems development + +steve: + name: Steve Ross Kellock + birthday: 1974-09-27 + profession: guy with keyboard +``` + +Each fixture is given a name followed by an indented list of colon-separated key/value pairs. Records are typically separated by a blank line. You can place comments in a fixture file by using the # character in the first column. + +If you are working with [associations](/association_basics.html), you can simply +define a reference node between two different fixtures. Here's an example with +a `belongs_to`/`has_many` association: + +```yaml +# In fixtures/categories.yml +about: + name: About + +# In fixtures/articles.yml +first: + title: Welcome to Rails! + body: Hello world! + category: about +``` + +Notice the `category` key of the `first` article found in `fixtures/articles.yml` has a value of `about`. This tells Rails to load the category `about` found in `fixtures/categories.yml`. + +NOTE: For associations to reference one another by name, you can use the fixture name instead of specifying the `id:` attribute on the associated fixtures. Rails will auto assign a primary key to be consistent between runs. For more information on this association behavior please read the [Fixtures API documentation](http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html). + +#### ERB'in It Up + +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#{n}" %> + email: <%= "user#{n}@example.com" %> +<% end %> +``` + +#### Fixtures in Action + +Rails automatically loads all fixtures from the `test/fixtures` directory by +default. Loading involves three steps: + +1. Remove any existing data from the table corresponding to the fixture +2. Load the fixture data into the table +3. Dump the fixture data into a method in case you want to access it directly + +TIP: In order to remove existing data from the database, Rails tries to disable referential integrity triggers (like foreign keys and check constraints). If you are getting annoying permission errors on running tests, make sure the database user has privilege to disable these triggers in testing environment. (In PostgreSQL, only superusers can disable all triggers. Read more about PostgreSQL permissions [here](http://blog.endpoint.com/2012/10/postgres-system-triggers-error.html)). + +#### Fixtures are Active Record objects + +Fixtures are instances of Active Record. As mentioned in point #3 above, you can access the object directly because it is automatically available as a method whose scope is local of the test case. For example: + +```ruby +# this will return the User object for the fixture named david +users(:david) + +# this will return the property for david called id +users(:david).id + +# one can also access methods available on the User class +email(david.partner.email, david.location_tonight) +``` + +To get multiple fixtures at once, you can pass in a list of fixture names. For example: + +```ruby +# this will return an array containing the fixtures david and steve +users(:david, :steve) +``` + + +Model Testing +------------- + +Model tests are used to test the various models of your application. + +Rails model tests are stored under the `test/models` directory. Rails provides +a generator to create a model test skeleton for you. + +```bash +$ bin/rails generate test_unit:model article title:string body:text +create test/models/article_test.rb +create test/fixtures/articles.yml +``` + +Model tests don't have their own superclass like `ActionMailer::TestCase` instead they inherit from `ActiveSupport::TestCase`. + + +Integration Testing +------------------- + +Integration tests are used to test how various parts of your application interact. They are generally used to test important workflows within our application. + +For creating Rails integration tests, we use the 'test/integration' directory for our application. Rails provides a generator to create an integration test skeleton for us. + +```bash +$ bin/rails generate integration_test user_flows + exists test/integration/ + create test/integration/user_flows_test.rb +``` + +Here's what a freshly-generated integration test looks like: + +```ruby +require 'test_helper' + +class UserFlowsTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end +``` + +Here the test is inheriting from `ActionDispatch::IntegrationTest`. This makes some additional helpers available for us to use in our integration tests. + +### Helpers Available for Integration Tests + +In addition to the standard testing helpers, inheriting from `ActionDispatch::IntegrationTest` comes with some additional helpers available when writing integration tests. Let's get briefly introduced to the three categories of helpers we get to choose from. + +For dealing with the integration test runner, see [`ActionDispatch::Integration::Runner`](http://api.rubyonrails.org/classes/ActionDispatch/Integration/Runner.html). + +When performing requests, we will have [`ActionDispatch::Integration::RequestHelpers`](http://api.rubyonrails.org/classes/ActionDispatch/Integration/RequestHelpers.html) available for our use. + +If we need to modify the session, or state of our integration test, take a look at [`ActionDispatch::Integration::Session`](http://api.rubyonrails.org/classes/ActionDispatch/Integration/Session.html) to help. + +### Implementing an integration test + +Let's add an integration test to our blog application. We'll start with a basic workflow of creating a new blog article, to verify that everything is working properly. + +We'll start by generating our integration test skeleton: + +```bash +$ bin/rails generate integration_test blog_flow +``` + +It should have created a test file placeholder for us. With the output of the +previous command we should see: + +```bash + invoke test_unit + create test/integration/blog_flow_test.rb +``` + +Now let's open that file and write our first assertion: + +```ruby +require 'test_helper' + +class BlogFlowTest < ActionDispatch::IntegrationTest + test "can see the welcome page" do + get "/" + assert_select "h1", "Welcome#index" + end +end +``` + +We will take a look at `assert_select` to query the resulting HTML of a request in the "Testing Views" section below. It is used for testing the response of our request by asserting the presence of key HTML elements and their content. + +When we visit our root path, we should see `welcome/index.html.erb` rendered for the view. So this assertion should pass. + +#### Creating articles integration + +How about testing our ability to create a new article in our blog and see the resulting article. + +```ruby +test "can create an article" do + get "/articles/new" + assert_response :success + + post "/articles", + params: { article: { title: "can create", body: "article successfully." } } + assert_response :redirect + follow_redirect! + assert_response :success + assert_select "p", "Title:\n can create" +end +``` + +Let's break this test down so we can understand it. + +We start by calling the `:new` action on our Articles controller. This response should be successful. + +After this we make a post request to the `:create` action of our Articles controller: + +```ruby +post "/articles", + params: { article: { title: "can create", body: "article successfully." } } +assert_response :redirect +follow_redirect! +``` + +The two lines following the request are to handle the redirect we setup when creating a new article. + +NOTE: Don't forget to call `follow_redirect!` if you plan to make subsequent requests after a redirect is made. + +Finally we can assert that our response was successful and our new article is readable on the page. + +#### Taking it further + +We were able to successfully test a very small workflow for visiting our blog and creating a new article. If we wanted to take this further we could add tests for commenting, removing articles, or editing comments. Integration tests are a great place to experiment with all kinds of use-cases for our applications. + Functional Tests for Your Controllers ------------------------------------- -In Rails, testing the various actions of a controller is a form of writing functional tests. Remember your controllers handle the incoming web requests to your application and eventually respond with a rendered view. When writing functional tests, you're testing how your actions handle the requests and the expected result, or response in some cases an HTML view. +In Rails, testing the various actions of a controller is a form of writing functional tests. Remember your controllers handle the incoming web requests to your application and eventually respond with a rendered view. When writing functional tests, you are testing how your actions handle the requests and the expected result or response, in some cases an HTML view. -### What to Include in your Functional Tests +### What to include in your Functional Tests You should test for things such as: @@ -515,23 +650,39 @@ You should test for things such as: * was the correct object stored in the response template? * was the appropriate message displayed to the user in the view? -Now that we have used Rails scaffold generator for our `Article` resource, it has already created the controller code and tests. You can take look at the file `articles_controller_test.rb` in the `test/controllers` directory. +The easiest way to see functional tests in action is to generate a controller using the scaffold generator: -The following command will generate a controller test case with a filled up -test for each of the seven default actions. +```bash +$ bin/rails generate scaffold_controller article title:string body:text +... +create app/controllers/articles_controller.rb +... +invoke test_unit +create test/controllers/articles_controller_test.rb +... +``` + +This will generate the controller code and tests for an `Article` resource. +You can take a look at the file `articles_controller_test.rb` in the `test/controllers` directory. + +If you already have a controller and just want to generate the test scaffold code for +each of the seven default actions, you can use the following command: ```bash $ bin/rails generate test_unit:scaffold article +... +invoke test_unit create test/controllers/articles_controller_test.rb +... ``` -Let me take you through one such test, `test_should_get_index` from the file `articles_controller_test.rb`. +Let's take a look at one such test, `test_should_get_index` from the file `articles_controller_test.rb`. ```ruby # articles_controller_test.rb -class ArticlesControllerTest < ActionController::TestCase +class ArticlesControllerTest < ActionDispatch::IntegrationTest test "should get index" do - get :index + get '/articles' assert_response :success assert_includes @response.body, 'Articles' end @@ -541,10 +692,10 @@ end In the `test_should_get_index` test, Rails simulates a request on the action called `index`, making sure the request was successful and also ensuring that the right response body has been generated. -The `get` method kicks off the web request and populates the results into the response. It accepts 4 arguments: +The `get` method kicks off the web request and populates the results into the `@response`. It accepts 4 arguments: * The action of the controller you are requesting. - This can be in the form of a string or a symbol. + This can be in the form of a string or a route (i.e. `articles_url`). * `params`: option with a hash of request parameters to pass into the action (e.g. query string parameters or article variables). @@ -553,7 +704,7 @@ The `get` method kicks off the web request and populates the results into the re * `flash`: option with a hash of flash values. -All the keyword arguments are optional. +All of these keyword arguments are optional. Example: Calling the `:show` action, passing an `id` of 12 as the `params` and setting a `user_id` of 5 in the session: @@ -564,7 +715,7 @@ get(:show, params: { id: 12 }, session: { user_id: 5 }) Another example: Calling the `:view` action, passing an `id` of 12 as the `params`, this time with no session, but with a flash message. ```ruby -get(:view, params: { id: 12 }, flash: { message: 'booya!' }) +get(view_url, params: { id: 12 }, flash: { message: 'booya!' }) ``` NOTE: If you try running `test_should_create_article` test from `articles_controller_test.rb` it will fail on account of the newly added model level validation and rightly so. @@ -574,7 +725,7 @@ Let us modify `test_should_create_article` test in `articles_controller_test.rb` ```ruby test "should create article" do assert_difference('Article.count') do - post :create, params: { article: { title: 'Some title' } } + post '/article', params: { article: { title: 'Some title' } } end assert_redirected_to article_path(Article.last) @@ -601,24 +752,25 @@ NOTE: Functional tests do not verify whether the specified request type is accep ### Testing XHR (AJAX) requests To test AJAX requests, you can specify the `xhr: true` option to `get`, `post`, -`patch`, `put`, and `delete` methods: +`patch`, `put`, and `delete` methods. For example: ```ruby test "ajax request" do - get :show, params: { id: articles(:first).id }, xhr: true + article = articles(:first) + get article_url(article), xhr: true assert_equal 'hello world', @response.body assert_equal "text/javascript", @response.content_type end ``` -### The Four Hashes of the Apocalypse +### The Three Hashes of the Apocalypse -After a request has been made and processed, you will have 4 Hash objects ready for use: +After a request has been made and processed, you will have 3 Hash objects ready for use: -* `cookies` - Any cookies that are set. -* `flash` - Any objects living in the flash. -* `session` - Any object living in session variables. +* `cookies` - Any cookies that are set +* `flash` - Any objects living in the flash +* `session` - Any object living in session variables As is the case with normal Hash objects, you can access the values by referencing the keys by string. You can also reference them by symbol name. For example: @@ -646,16 +798,16 @@ can be set directly on the `@request` instance variable: ```ruby # setting a HTTP Header @request.headers["Accept"] = "text/plain, text/html" -get :index # simulate the request with custom header +get articles_url # simulate the request with custom header # setting a CGI variable @request.headers["HTTP_REFERER"] = "http://example.com/home" -post :create # simulate the request with custom env variable +post article_url # simulate the request with custom env variable ``` ### Testing `flash` notices -If you remember from earlier one of the Four Hashes of the Apocalypse was `flash`. +If you remember from earlier, one of the Three Hashes of the Apocalypse was `flash`. We want to add a `flash` message to our blog application whenever someone successfully creates a new Article. @@ -665,7 +817,7 @@ Let's start by adding this assertion to our `test_should_create_article` test: ```ruby test "should create article" do assert_difference('Article.count') do - post :create, params: { article: { title: 'Some title' } } + post article_url, params: { article: { title: 'Some title' } } end assert_redirected_to article_path(Article.last) @@ -735,12 +887,12 @@ Let's write a test for the `:show` action: ```ruby test "should show article" do article = articles(:one) - get :show, params: { id: article.id } + get '/article', params: { id: article.id } assert_response :success end ``` -Remember from our discussion earlier on fixtures the `articles()` method will give us access to our Articles fixtures. +Remember from our discussion earlier on fixtures, the `articles()` method will give us access to our Articles fixtures. How about deleting an existing Article? @@ -748,7 +900,7 @@ How about deleting an existing Article? test "should destroy article" do article = articles(:one) assert_difference('Article.count', -1) do - delete :destroy, params: { id: article.id } + delete article_url(article) end assert_redirected_to articles_path @@ -760,47 +912,56 @@ We can also add a test for updating an existing Article. ```ruby test "should update article" do article = articles(:one) - patch :update, params: { id: article.id, article: { title: "updated" } } + + patch '/article', params: { id: article.id, article: { title: "updated" } } + assert_redirected_to article_path(article) + # Reload association to fetch updated data and assert that title is updated. + article.reload + assert_equal "updated", article.title end ``` Notice we're starting to see some duplication in these three tests, they both access the same Article fixture data. We can D.R.Y. this up by using the `setup` and `teardown` methods provided by `ActiveSupport::Callbacks`. -Our test should now look something like this, disregard the other tests we're leaving them out for brevity. +Our test should now look something as what follows. Disregard the other tests for now, we're leaving them out for brevity. ```ruby require 'test_helper' -class ArticlesControllerTest < ActionController::TestCase +class ArticlesControllerTest < ActionDispatch::IntegrationTest # called before every single test - def setup + setup do @article = articles(:one) end # called after every single test - def teardown + teardown do # when controller is using cache it may be a good idea to reset it afterwards Rails.cache.clear end test "should show article" do # Reuse the @article instance variable from setup - get :show, params: { id: @article.id } + get article_url(@article) assert_response :success end test "should destroy article" do assert_difference('Article.count', -1) do - delete :destroy, params: { id: @article.id } + delete article_url(@article) end assert_redirected_to articles_path end test "should update article" do - patch :update, params: { id: @article.id, article: { title: "updated" } } + patch '/article', params: { id: @article.id, article: { title: "updated" } } + assert_redirected_to article_path(@article) + # Reload association to fetch updated data and assert that title is updated. + @article.reload + assert_equal "updated", @article.title end end ``` @@ -813,7 +974,7 @@ To avoid code duplication, you can add your own test helpers. Sign in helper can be a good example: ```ruby -test/test_helper.rb +#test/test_helper.rb module SignInHelper def sign_in(user) @@ -821,7 +982,7 @@ module SignInHelper end end -class ActionController::TestCase +class ActionDispatch::IntegrationTest include SignInHelper end ``` @@ -829,13 +990,13 @@ end ```ruby require 'test_helper' -class ProfileControllerTest < ActionController::TestCase +class ProfileControllerTest < ActionDispatch::IntegrationTest test "should show profile" do # helper is now reusable from any controller test case sign_in users(:david) - get :show + get profile_url assert_response :success end end @@ -844,33 +1005,7 @@ end Testing Routes -------------- -Like everything else in your Rails application, it is recommended that you test your routes. Below are example tests for the routes of default `show` and `create` action of `Articles` controller above and it should look like: - -```ruby -class ArticleRoutesTest < ActionController::TestCase - test "should route to article" do - assert_routing '/articles/1', { controller: "articles", action: "show", id: "1" } - end - - test "should route to create article" do - assert_routing({ method: 'post', path: '/articles' }, { controller: "articles", action: "create" }) - end -end -``` - -I've added this file here `test/controllers/articles_routes_test.rb` and if we run the test we should see: - -```bash -$ bin/rails test test/controllers/articles_routes_test.rb - -# Running: - -.. - -Finished in 0.069381s, 28.8263 runs/s, 86.4790 assertions/s. - -2 runs, 6 assertions, 0 failures, 0 errors, 0 skips -``` +Like everything else in your Rails application, you can test your routes. For more information on routing assertions available in Rails, see the API documentation for [`ActionDispatch::Assertions::RoutingAssertions`](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html). @@ -960,10 +1095,9 @@ have to use a mixin like this: ```ruby class UserHelperTest < ActionView::TestCase - include UserHelper - - test "should return the user name" do - # ... + test "should return the user's full name" do + user = users(:david) + assert_equal "David Heinemeier Hansson", user_full_name(user) end end ``` @@ -971,118 +1105,6 @@ end Moreover, since the test class extends from `ActionView::TestCase`, you have access to Rails' helper methods such as `link_to` or `pluralize`. -Integration Testing -------------------- - -Integration tests are used to test how various parts of your application interact. They are generally used to test important work flows within your application. - -For creating Rails integration tests, we use the 'test/integration' directory for your application. Rails provides a generator to create an integration test skeleton for you. - -```bash -$ bin/rails generate integration_test user_flows - exists test/integration/ - create test/integration/user_flows_test.rb -``` - -Here's what a freshly-generated integration test looks like: - -```ruby -require 'test_helper' - -class UserFlowsTest < ActionDispatch::IntegrationTest - # test "the truth" do - # assert true - # end -end -``` - -Inheriting from `ActionDispatch::IntegrationTest` comes with some advantages. This makes available some additional helpers to use in your integration tests. - -### Helpers Available for Integration Tests - -In addition to the standard testing helpers, inheriting `ActionDispatch::IntegrationTest` comes with some additional helpers available when writing integration tests. Let's briefly introduce you to the three categories of helpers you get to choose from. - -For dealing with the integration test runner, see [`ActionDispatch::Integration::Runner`](http://api.rubyonrails.org/classes/ActionDispatch/Integration/Runner.html). - -When performing requests, you will have [`ActionDispatch::Integration::RequestHelpers`](http://api.rubyonrails.org/classes/ActionDispatch/Integration/RequestHelpers.html) available for your use. - -If you'd like to modify the session, or state of your integration test you should look for [`ActionDispatch::Integration::Session`](http://api.rubyonrails.org/classes/ActionDispatch/Integration/Session.html) to help. - -### Implementing an integration test - -Let's add an integration test to our blog application. We'll start with a basic workflow of creating a new blog article, to verify that everything is working properly. - -We'll start by generating our integration test skeleton: - -```bash -$ bin/rails generate integration_test blog_flow -``` - -It should have created a test file placeholder for us, with the output of the previous command you should see: - -```bash - invoke test_unit - create test/integration/blog_flow_test.rb -``` - -Now let's open that file and write our first assertion: - -```ruby -require 'test_helper' - -class BlogFlowTest < ActionDispatch::IntegrationTest - test "can see the welcome page" do - get "/" - assert_select "h1", "Welcome#index" - end -end -``` - -If you remember from earlier in the "Testing Views" section we covered `assert_select` to query the resulting HTML of a request. - -When visit our root path, we should see `welcome/index.html.erb` rendered for the view. So this assertion should pass. - -#### Creating articles integration - -How about testing our ability to create a new article in our blog and see the resulting article. - -```ruby -test "can create an article" do - get "/articles/new" - assert_response :success - - post "/articles", - params: { article: { title: "can create", body: "article successfully." } } - assert_response :redirect - follow_redirect! - assert_response :success - assert_select "p", "Title:\n can create" -end -``` - -Let's break this test down so we can understand it. - -We start by calling the `:new` action on our Articles controller. This response should be successful, and we can verify the correct template is rendered including the form partial. - -After this we make a post request to the `:create` action of our Articles controller: - -```ruby -post "/articles", - params: { article: { title: "can create", body: "article successfully." } } -assert_response :redirect -follow_redirect! -``` - -The two lines following the request are to handle the redirect we setup when creating a new article. - -NOTE: Don't forget to call `follow_redirect!` if you plan to make subsequent requests after a redirect is made. - -Finally we can assert that our response was successful, template was rendered, and our new article is readable on the page. - -#### Taking it further - -We were able to successfully test a very small workflow for visiting our blog and creating a new article. If we wanted to take this further we could add tests for commenting, removing articles, or editing comments. Integration tests are a great place to experiment with all kinds of use-cases for our applications. - Testing Your Mailers -------------------- @@ -1110,7 +1132,7 @@ In order to test that your mailer is working as expected, you can use unit tests For the purposes of unit testing a mailer, fixtures are used to provide an example of how the output _should_ look. Because these are example emails, and not Active Record data like the other fixtures, they are kept in their own subdirectory apart from the other fixtures. The name of the directory within `test/fixtures` directly corresponds to the name of the mailer. So, for a mailer named `UserMailer`, the fixtures should reside in `test/fixtures/user_mailer` directory. -When you generated your mailer, the generator creates stub fixtures for each of the mailers actions. If you didn't use the generator you'll have to make those files yourself. +When you generated your mailer, the generator creates stub fixtures for each of the mailers actions. If you didn't use the generator, you'll have to create those files yourself. #### The Basic Test Case @@ -1121,10 +1143,13 @@ require 'test_helper' class UserMailerTest < ActionMailer::TestCase test "invite" do + # Create the email and store it for further assertions + email = UserMailer.create_invite('me@example.com', + 'friend@example.com', Time.now) + # Send the email, then test that it got queued assert_emails 1 do - email = UserMailer.create_invite('me@example.com', - 'friend@example.com', Time.now).deliver_now + email.deliver_now end # Test the body of the sent email contains what we expect it to @@ -1170,10 +1195,10 @@ Functional testing for mailers involves more than just checking that the email b ```ruby require 'test_helper' -class UserControllerTest < ActionController::TestCase +class UserControllerTest < ActionDispatch::IntegrationTest test "invite friend" do assert_difference 'ActionMailer::Base.deliveries.size', +1 do - post :invite_friend, params: { email: 'friend@example.com' } + post invite_friend_url, params: { email: 'friend@example.com' } end invite_email = ActionMailer::Base.deliveries.last @@ -1188,7 +1213,7 @@ Testing Jobs ------------ Since your custom jobs can be queued at different levels inside your application, -you'll need to test both jobs themselves (their behavior when they get enqueued) +you'll need to test both, the jobs themselves (their behavior when they get enqueued) and that other entities correctly enqueue them. ### A Basic Test Case @@ -1236,15 +1261,23 @@ class ProductTest < ActiveJob::TestCase end ``` -Other Testing Approaches ------------------------- +Testing Time-Dependent Code +--------------------------- + +Rails provides built-in helper methods that enable you to assert that your time-sensitive code works as expected. -The built-in `minitest` based testing is not the only way to test Rails applications. Rails developers have come up with a wide variety of other approaches and aids for testing, including: +Here is an example using the [`travel_to`](http://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html#method-i-travel_to) helper: + +```ruby +# Lets say that a user is eligible for gifting a month after they register. +user = User.create(name: 'Gaurish', activation_date: Date.new(2004, 10, 24)) +assert_not user.applicable_for_gifting? +travel_to Date.new(2004, 11, 24) do + assert_equal Date.new(2004, 10, 24), user.activation_date # inside the travel_to block `Date.current` is mocked + assert user.applicable_for_gifting? +end +assert_equal Date.new(2004, 10, 24), user.activation_date # The change was visible only inside the `travel_to` block. +``` -* [NullDB](http://avdi.org/projects/nulldb/), a way to speed up testing by avoiding database use. -* [Factory Girl](https://github.com/thoughtbot/factory_girl/tree/master), a replacement for fixtures. -* [Fixture Builder](https://github.com/rdy/fixture_builder), a tool that compiles Ruby factories into fixtures before a test run. -* [MiniTest::Spec Rails](https://github.com/metaskills/minitest-spec-rails), use the MiniTest::Spec DSL within your rails tests. -* [Shoulda](http://www.thoughtbot.com/projects/shoulda), an extension to `test/unit` with additional helpers, macros, and assertions. -* [RSpec](http://relishapp.com/rspec), a behavior-driven development framework -* [Capybara](http://jnicklas.github.com/capybara/), Acceptance test framework for web applications +Please see [`ActiveSupport::TimeHelpers` API Documentation](http://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html) +for in-depth information about the available time helpers. diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index 17309d4b47..e631445492 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -53,29 +53,55 @@ Don't forget to review the difference, to see if there were any unexpected chang Upgrading from Rails 4.2 to Rails 5.0 ------------------------------------- -### Halting callback chains by returning `false` +### Ruby 2.2.2+ -In Rails 4.2, when a 'before' callback returns `false` in ActiveRecord, -ActiveModel and ActiveModel::Validations, then the entire callback chain -is halted. In other words, successive 'before' callbacks are not executed, -and neither is the action wrapped in callbacks. +ToDo... -In Rails 5.0, returning `false` in a callback will not have this side effect -of halting the callback chain. Instead, callback chains must be explicitly -halted by calling `throw(:abort)`. +### Active Record models now inherit from ApplicationRecord by default -When you upgrade from Rails 4.2 to Rails 5.0, returning `false` in a callback -will still halt the callback chain, but you will receive a deprecation warning -about this upcoming change. +In Rails 4.2 an Active Record model inherits from `ActiveRecord::Base`. In Rails 5.0, +all models inherit from `ApplicationRecord`. + +`ApplicationRecord` is a new superclass for all app models, analogous to app +controllers subclassing `ApplicationController` instead of +`ActionController::Base`. This gives apps a single spot to configure app-wide +model behavior. + +When upgrading from Rails 4.2 to Rails 5.0 you need to create an +`application_record.rb` file in `app/models/` and add the following content: + +``` +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end +``` + +### Halting callback chains via `throw(:abort)` + +In Rails 4.2, when a 'before' callback returns `false` in Active Record +and Active Model, then the entire callback chain is halted. In other words, +successive 'before' callbacks are not executed, and neither is the action wrapped +in callbacks. + +In Rails 5.0, returning `false` in an Active Record or Active Model callback +will not have this side effect of halting the callback chain. Instead, callback +chains must be explicitly halted by calling `throw(:abort)`. + +When you upgrade from Rails 4.2 to Rails 5.0, returning `false` in those kind of +callbacks will still halt the callback chain, but you will receive a deprecation +warning about this upcoming change. When you are ready, you can opt into the new behavior and remove the deprecation warning by adding the following configuration to your `config/application.rb`: - config.active_support.halt_callback_chains_on_return_false = false + ActiveSupport.halt_callback_chains_on_return_false = false + +Note that this option will not affect Active Support callbacks since they never +halted the chain when any value was returned. See [#17227](https://github.com/rails/rails/pull/17227) for more details. -### ActiveJob jobs now inherent from ApplicationJob by default +### ActiveJob jobs now inherit from ApplicationJob by default In Rails 4.2 an ActiveJob inherits from `ActiveJob::Base`. In Rails 5.0 this behavior has changed to now inherit from `ApplicationJob`. @@ -314,11 +340,11 @@ Upgrading from Rails 4.0 to Rails 4.1 ### CSRF protection from remote `<script>` tags -Or, "whaaat my tests are failing!!!?" +Or, "whaaat my tests are failing!!!?" or "my `<script>` widget is busted!!" Cross-site request forgery (CSRF) protection now covers GET requests with -JavaScript responses, too. This prevents a third-party site from referencing -your JavaScript URL and attempting to run it to extract sensitive data. +JavaScript responses, too. This prevents a third-party site from remotely +referencing your JavaScript with a `<script>` tag to extract sensitive data. This means that your functional and integration tests that use @@ -334,8 +360,9 @@ xhr :get, :index, format: :js to explicitly test an `XmlHttpRequest`. -If you really mean to load JavaScript from remote `<script>` tags, skip CSRF -protection on that action. +Note: Your own `<script>` tags are treated as cross-origin and blocked by +default, too. If you really mean to load JavaScript from `<script>` tags, +you must now explicitly skip CSRF protection on those actions. ### Spring @@ -375,7 +402,7 @@ secrets, you need to: 3. Remove the `secret_token.rb` initializer. -4. Use `rake secret` to generate new keys for the `development` and `test` sections. +4. Use `rails secret` to generate new keys for the `development` and `test` sections. 5. Restart your server. @@ -895,7 +922,7 @@ CatalogProduct < ActiveRecord::Base end ``` -* Note that the the prefix takes scopes into account as well, so relations between `Catalog::Category` and `Catalog::Product` or `Catalog::Category` and `CatalogProduct` need to be updated similarly. +* Note that the prefix takes scopes into account as well, so relations between `Catalog::Category` and `Catalog::Product` or `Catalog::Category` and `CatalogProduct` need to be updated similarly. ### Active Resource @@ -940,7 +967,7 @@ Please read [Pull Request #9978](https://github.com/rails/rails/pull/9978) for d * Rails 4.0 has removed the XML parameters parser. You will need to add the `actionpack-xml_parser` gem if you require this feature. -* Rails 4.0 changes the default `layout` lookup set using symbols or procs that return nil. To get the "no layout" behavior, return false instead of nil. +* Rails 4.0 changes the default `layout` lookup set using symbols or procs that return nil. To get the "no layout" behavior, return false instead of nil. * Rails 4.0 changes the default memcached client from `memcache-client` to `dalli`. To upgrade, simply add `gem 'dalli'` to your `Gemfile`. @@ -1086,7 +1113,7 @@ config.active_record.auto_explain_threshold_in_seconds = 0.5 ### config/environments/test.rb -The `mass_assignment_sanitizer` configuration setting should also be be added to `config/environments/test.rb`: +The `mass_assignment_sanitizer` configuration setting should also be added to `config/environments/test.rb`: ```ruby # Raise exception on mass assignment protection for Active Record models @@ -1187,8 +1214,10 @@ You can help test performance with these additions to your test environment: ```ruby # Configure static asset server for tests with Cache-Control for performance -config.serve_static_files = true -config.static_cache_control = 'public, max-age=3600' +config.public_file_server.enabled = true +config.public_file_server.headers = { + 'Cache-Control' => 'public, max-age=3600' +} ``` ### config/initializers/wrap_parameters.rb diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md index f3d3a83afc..48fc6bc9c0 100644 --- a/guides/source/working_with_javascript_in_rails.md +++ b/guides/source/working_with_javascript_in_rails.md @@ -81,7 +81,7 @@ Awkward, right? We could pull the function definition out of the click handler, and turn it into CoffeeScript: ```coffeescript -paintIt = (element, backgroundColor, textColor) -> +@paintIt = (element, backgroundColor, textColor) -> element.style.backgroundColor = backgroundColor if textColor? element.style.color = textColor @@ -107,7 +107,7 @@ attribute to our link, and then bind a handler to the click event of every link that has that attribute: ```coffeescript -paintIt = (element, backgroundColor, textColor) -> +@paintIt = (element, backgroundColor, textColor) -> element.style.backgroundColor = backgroundColor if textColor? element.style.color = textColor @@ -258,7 +258,7 @@ this generates ```html <form action="/articles/1" class="button_to" data-remote="true" method="post"> - <div><input type="submit" value="An article"></div> + <input type="submit" value="An article" /> </form> ``` @@ -357,7 +357,7 @@ This gem uses Ajax to speed up page rendering in most applications. Turbolinks attaches a click handler to all `<a>` on the page. If your browser supports -[PushState](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history#The_pushState()_method), +[PushState](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history#The_pushState%28%29_method), Turbolinks will make an Ajax request for the page, parse the response, and replace the entire `<body>` of the page with the `<body>` of the response. It will then use PushState to change the URL to the correct one, preserving |