diff options
Diffstat (limited to 'guides/source')
-rw-r--r-- | guides/source/4_2_release_notes.md | 5 | ||||
-rw-r--r-- | guides/source/action_controller_overview.md | 2 | ||||
-rw-r--r-- | guides/source/action_mailer_basics.md | 3 | ||||
-rw-r--r-- | guides/source/action_view_overview.md | 45 | ||||
-rw-r--r-- | guides/source/active_record_postgresql.md | 15 | ||||
-rw-r--r-- | guides/source/active_record_validations.md | 2 | ||||
-rw-r--r-- | guides/source/api_documentation_guidelines.md | 3 | ||||
-rw-r--r-- | guides/source/caching_with_rails.md | 22 | ||||
-rw-r--r-- | guides/source/configuring.md | 4 | ||||
-rw-r--r-- | guides/source/contributing_to_ruby_on_rails.md | 2 | ||||
-rw-r--r-- | guides/source/debugging_rails_applications.md | 85 | ||||
-rw-r--r-- | guides/source/getting_started.md | 5 | ||||
-rw-r--r-- | guides/source/i18n.md | 165 | ||||
-rw-r--r-- | guides/source/ruby_on_rails_guides_guidelines.md | 6 | ||||
-rw-r--r-- | guides/source/testing.md | 46 | ||||
-rw-r--r-- | guides/source/upgrading_ruby_on_rails.md | 14 |
16 files changed, 256 insertions, 168 deletions
diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md index d5b3766a5b..684bd286bc 100644 --- a/guides/source/4_2_release_notes.md +++ b/guides/source/4_2_release_notes.md @@ -214,8 +214,9 @@ end Due to a [change in Rack](https://github.com/rack/rack/commit/28b014484a8ac0bbb388e7eaeeef159598ec64fc), `rails server` now listens on `localhost` instead of `0.0.0.0` by default. This -should have minimal impact on the standard development workflow as http://localhost:3000 -will continue to work as before on your own machine. +should have minimal impact on the standard development workflow as both +http://127.0.0.1:3000 and http://localhost:3000 will continue to work as before +on your own machine. However, with this change you will no longer be able to access the Rails server from a different machine, for example if your development environment diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index fab0e20aba..7d95d4792e 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -749,7 +749,7 @@ class ApplicationController < ActionController::Base end ``` -Note that the filter in this case uses `send` because the `logged_in?` method is private and the filter is not run in the scope of the controller. This is not the recommended way to implement this particular filter, but in more simple cases it might be useful. +Note that the filter in this case uses `send` because the `logged_in?` method is private and the filter does not run in the scope of the controller. This is not the recommended way to implement this particular filter, but in more simple cases it might be useful. The second way is to use a class (actually, any object that responds to the right methods will do) to handle the filtering. This is useful in cases that are more complex and cannot be implemented in a readable and reusable way using the two other methods. As an example, you could rewrite the login filter again to use a class: diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md index 73b240ff2c..089ce53f07 100644 --- a/guides/source/action_mailer_basics.md +++ b/guides/source/action_mailer_basics.md @@ -530,6 +530,9 @@ url helper. <%= user_url(@user, host: 'example.com') %> ``` +NOTE: non-`GET` links require [jQuery UJS](https://github.com/rails/jquery-ujs) +and won't work in mailer templates. They will result in normal `GET` requests. + ### Sending Multipart Emails Action Mailer will automatically send multipart emails if you have different diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md index 44c02165db..3541bbaa93 100644 --- a/guides/source/action_view_overview.md +++ b/guides/source/action_view_overview.md @@ -356,39 +356,34 @@ Supposing we use the same `_box` partial from above, this would produce the same View Paths ---------- -When rendering the view for a request, the controller needs to resolve where to find each of the directories are located. +When rendering a response, the controller needs to resolve where the different +views are located. By default it only looks inside the `app/views` directory. -We are able to modify the order these locations are resolved by using `prepend_view_path` and `append_view_path`. - -This allows us to add new paths to the beginning or end of the list used to resolve these paths. +We can add other locations and give them a certain precedence when resolving +paths using the `prepend_view_path` and `append_view_path` methods. ### Prepend view path -This can be helpful for example, when we want to prepend a different directory for subdomains. +This can be helpful for example, when we want to put views inside a different +directory for subdomains. We can do this by using: -```prepend_view_path "app/views/#{request.subdomain}"``` - -Then our list becomes something like: - -``` -[ - ~/rails_app/app/views/<subdomain>, - ~/rails_app/app/views, - # ... -] +```ruby +prepend_view_path "app/views/#{request.subdomain}" ``` -This will put the subdomain path at the beginning of the list. +Then Action View will look first in this directory when resolving views. ### Append view path Similarly, we can append paths: -```append_view_path "app/views/direct"```. +```ruby +append_view_path "app/views/direct" +``` -This will add ```app/views/direct``` and the end of lookup paths for views. +This will add `app/views/direct` to the end of the lookup paths. Overview of helpers provided by Action View ------------------------------------------- @@ -498,7 +493,7 @@ Returns a stylesheet link tag for the sources specified as arguments. If you don stylesheet_link_tag "application" # => <link href="/assets/application.css" media="screen" rel="stylesheet" /> ``` -You can also include all styles in the stylesheet directory using :all as the source: +You can also include all styles in the stylesheet directory using `:all` as the source: ```ruby stylesheet_link_tag :all @@ -513,7 +508,7 @@ stylesheet_link_tag :all, cache: true #### stylesheet_path -Computes the path to a stylesheet asset in the `app/assets/stylesheets` directory. If the source filename has no extension, .css will be appended. Full paths from the document root will be passed through. Used internally by stylesheet_link_tag to build the stylesheet path. +Computes the path to a stylesheet asset in the `app/assets/stylesheets` directory. If the source filename has no extension, `.css` will be appended. Full paths from the document root will be passed through. Used internally by stylesheet_link_tag to build the stylesheet path. ```ruby stylesheet_path "application" # => /assets/application.css @@ -814,9 +809,9 @@ third: Form helpers are designed to make working with models much easier compared to using just standard HTML elements by providing a set of methods for creating forms based on your models. This helper generates the HTML for forms, providing a method for each sort of input (e.g., text, password, select, and so on). When the form is submitted (i.e., when the user hits the submit button or form.submit is called via JavaScript), the form inputs will be bundled into the params object and passed back to the controller. -There are two types of form helpers: those that specifically work with model attributes and those that don't. This helper deals with those that work with model attributes; to see an example of form helpers that don't work with model attributes, check the ActionView::Helpers::FormTagHelper documentation. +There are two types of form helpers: those that specifically work with model attributes and those that don't. This helper deals with those that work with model attributes; to see an example of form helpers that don't work with model attributes, check the `ActionView::Helpers::FormTagHelper` documentation. -The core method of this helper, form_for, gives you the ability to create a form for a model instance; for example, let's say that you have a model Person and want to create a new instance of it: +The core method of this helper, `form_for`, gives you the ability to create a form for a model instance; for example, let's say that you have a model Person and want to create a new instance of it: ```html+erb # Note: a @person variable will have been created in the controller (e.g. @person = Person.new) @@ -858,7 +853,7 @@ check_box("article", "validated") #### fields_for -Creates a scope around a specific model object like form_for, but doesn't create the form tags themselves. This makes fields_for suitable for specifying additional model objects in the same form: +Creates a scope around a specific model object like `form_for`, but doesn't create the form tags themselves. This makes `fields_for` suitable for specifying additional model objects in the same form: ```html+erb <%= form_for @person, url: { action: "update" } do |person_form| %> @@ -1152,7 +1147,7 @@ Returns a string of option tags that have been compiled by iterating over the `c # options_from_collection_for_select(collection, value_method, text_method, selected = nil) ``` -For example, imagine a loop iterating over each person in @project.people to generate an input tag: +For example, imagine a loop iterating over each person in `@project.people` to generate an input tag: ```ruby options_from_collection_for_select(@project.people, "id", "name") @@ -1453,7 +1448,7 @@ This sanitize helper will HTML encode all tags and strip all attributes that are sanitize @article.body ``` -If either the :attributes or :tags options are passed, only the mentioned tags and attributes are allowed and nothing else. +If either the `:attributes` or `:tags` options are passed, only the mentioned tags and attributes are allowed and nothing else. ```ruby sanitize @article.body, tags: %w(table tr td), attributes: %w(id class style) diff --git a/guides/source/active_record_postgresql.md b/guides/source/active_record_postgresql.md index 66a11e5785..5e58d5baeb 100644 --- a/guides/source/active_record_postgresql.md +++ b/guides/source/active_record_postgresql.md @@ -242,10 +242,12 @@ article.save! ### UUID -* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-uuid.html) -* [generator functions](http://www.postgresql.org/docs/9.3/static/uuid-ossp.html) +* [type definition](http://www.postgresql.org/docs/9.4/static/datatype-uuid.html) +* [pgcrypto generator function](http://www.postgresql.org/docs/9.4/static/pgcrypto.html#AEN159361) +* [uuid-ossp generator functions](http://www.postgresql.org/docs/9.4/static/uuid-ossp.html) -NOTE: you need to enable the `uuid-ossp` extension to use uuid. +NOTE: you need to enable the `pgcrypto` (only PostgreSQL >= 9.4) or `uuid-ossp` +extension to use uuid. ```ruby # db/migrate/20131220144913_create_revisions.rb @@ -356,12 +358,13 @@ A point is casted to an array containing `x` and `y` coordinates. UUID Primary Keys ----------------- -NOTE: you need to enable the `uuid-ossp` extension to generate UUIDs. +NOTE: you need to enable the `pgcrypto` (only PostgreSQL >= 9.4) or `uuid-ossp` +extension to generate random UUIDs. ```ruby # db/migrate/20131220144913_create_devices.rb -enable_extension 'uuid-ossp' unless extension_enabled?('uuid-ossp') -create_table :devices, id: :uuid, default: 'uuid_generate_v4()' do |t| +enable_extension 'pgcrypto' unless extension_enabled?('pgcrypto') +create_table :devices, id: :uuid, default: 'gen_random_uuid()' do |t| t.string :kind end diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md index d251c5c0b1..343b761e93 100644 --- a/guides/source/active_record_validations.md +++ b/guides/source/active_record_validations.md @@ -617,7 +617,7 @@ end The validation happens by performing an SQL query into the model's table, searching for an existing record with the same value in that attribute. -There is a `:scope` option that you can use to specify other attributes that +There is a `:scope` option that you can use to specify one or more attributes that are used to limit the uniqueness check: ```ruby diff --git a/guides/source/api_documentation_guidelines.md b/guides/source/api_documentation_guidelines.md index b385bdbe83..46c9013087 100644 --- a/guides/source/api_documentation_guidelines.md +++ b/guides/source/api_documentation_guidelines.md @@ -16,7 +16,8 @@ RDoc ---- The [Rails API documentation](http://api.rubyonrails.org) is generated with -[RDoc](http://docs.seattlerb.org/rdoc/). +[RDoc](http://docs.seattlerb.org/rdoc/). To generate it, make sure you are +in the rails root directory, run `bundle install` and execute: ```bash bundle exec rake rdoc diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md index 716beb9178..782406659d 100644 --- a/guides/source/caching_with_rails.md +++ b/guides/source/caching_with_rails.md @@ -22,7 +22,7 @@ fragment caching. Rails provides by default fragment caching. In order to use page and action caching, you will need to add `actionpack-page_caching` and `actionpack-action_caching` to your Gemfile. -To start playing with caching you'll want to ensure that `config.action_controller.perform_caching` is set to `true`, if you're running in development mode. This flag is normally set in the corresponding `config/environments/*.rb` and caching is disabled by default for development and test, and enabled for production. +To start playing with caching you'll want to ensure that `config.action_controller.perform_caching` is set to `true` if you're running in development mode. This flag is normally set in the corresponding `config/environments/*.rb` and caching is disabled by default for development and test, and enabled for production. ```ruby config.action_controller.perform_caching = true @@ -144,7 +144,7 @@ It's called "Russian Doll Caching" because it nests multiple fragments. The adva ### Low-Level Caching -Sometimes you need to cache a particular value or query result, instead of caching view fragments. Rails caching mechanism works great for storing __any__ kind of information. +Sometimes you need to cache a particular value or query result instead of caching view fragments. Rails' caching mechanism works great for storing __any__ kind of information. The most efficient way to implement low-level caching is using the `Rails.cache.fetch` method. This method does both reading and writing to the cache. When passed only a single argument, the key is fetched and value from the cache is returned. If a block is passed, the result of the block will be cached to the given key and the result is returned. @@ -160,7 +160,7 @@ class Product < ActiveRecord::Base end ``` -NOTE: Notice that in this example we used `cache_key` method, so the resulting cache-key will be something like `products/233-20140225082222765838000/competing_price`. `cache_key` generates a string based on the model’s `id` and `updated_at` attributes. This is a common convention and has the benefit of invalidating the cache whenever the product is updated. In general, when you use low-level caching for instance level information, you need to generate a cache key. +NOTE: Notice that in this example we used the `cache_key` method, so the resulting cache-key will be something like `products/233-20140225082222765838000/competing_price`. `cache_key` generates a string based on the model’s `id` and `updated_at` attributes. This is a common convention and has the benefit of invalidating the cache whenever the product is updated. In general, when you use low-level caching for instance level information, you need to generate a cache key. ### SQL Caching @@ -219,7 +219,7 @@ There are some common options used by all cache implementations. These can be pa * `:compress` - This option can be used to indicate that compression should be used in the cache. This can be useful for transferring large cache entries over a slow network. -* `:compress_threshold` - This options is used in conjunction with the `:compress` option to indicate a threshold under which cache entries should not be compressed. This defaults to 16 kilobytes. +* `:compress_threshold` - This option is used in conjunction with the `:compress` option to indicate a threshold under which cache entries should not be compressed. This defaults to 16 kilobytes. * `:expires_in` - This option sets an expiration time in seconds for the cache entry when it will be automatically removed from the cache. @@ -227,7 +227,7 @@ There are some common options used by all cache implementations. These can be pa ### ActiveSupport::Cache::MemoryStore -This cache store keeps entries in memory in the same Ruby process. The cache store has a bounded size specified by the `:size` options to the initializer (default is 32Mb). When the cache exceeds the allotted size, a cleanup will occur and the least recently used entries will be removed. +This cache store keeps entries in memory in the same Ruby process. The cache store has a bounded size specified by the `:size` option to the initializer (default is 32Mb). When the cache exceeds the allotted size, a cleanup will occur and the least recently used entries will be removed. ```ruby config.cache_store = :memory_store, { size: 64.megabytes } @@ -243,7 +243,7 @@ This cache store uses the file system to store entries. The path to the director config.cache_store = :file_store, "/path/to/cache/directory" ``` -With this cache store, multiple server processes on the same host can share a cache. Servers processes running on different hosts could share a cache by using a shared file system, but that set up would not be ideal and is not recommended. The cache store is appropriate for low to medium traffic sites that are served off one or two hosts. +With this cache store, multiple server processes on the same host can share a cache. Server processes running on different hosts could share a cache by using a shared file system, but that set up would not be ideal and is not recommended. The cache store is appropriate for low to medium traffic sites that are served off one or two hosts. Note that the cache will grow until the disk is full unless you periodically clear out old entries. @@ -255,7 +255,7 @@ This cache store uses Danga's `memcached` server to provide a centralized cache When initializing the cache, you need to specify the addresses for all memcached servers in your cluster. If none is specified, it will assume memcached is running on the local host on the default port, but this is not an ideal set up for larger sites. -The `write` and `fetch` methods on this cache accept two additional options that take advantage of features specific to memcached. You can specify `:raw` to send a value directly to the server with no serialization. The value must be a string or number. You can use memcached direct operation like `increment` and `decrement` only on raw values. You can also specify `:unless_exist` if you don't want memcached to overwrite an existing entry. +The `write` and `fetch` methods on this cache accept two additional options that take advantage of features specific to memcached. You can specify `:raw` to send a value directly to the server with no serialization. The value must be a string or number. You can use memcached direct operations like `increment` and `decrement` only on raw values. You can also specify `:unless_exist` if you don't want memcached to overwrite an existing entry. ```ruby config.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.com" @@ -293,7 +293,7 @@ For more information about Ehcache for JRuby and Rails, see [http://ehcache.org/ ### ActiveSupport::Cache::NullStore -This cache store implementation is meant to be used only in development or test environments and it never stores anything. This can be very useful in development when you have code that interacts directly with `Rails.cache`, but caching may interfere with being able to see the results of code changes. With this cache store, all `fetch` and `read` operations will result in a miss. +This cache store implementation is meant to be used only in development or test environments and it never stores anything. This can be very useful in development when you have code that interacts directly with `Rails.cache` but caching may interfere with being able to see the results of code changes. With this cache store, all `fetch` and `read` operations will result in a miss. ```ruby config.cache_store = :null_store @@ -303,7 +303,7 @@ config.cache_store = :null_store You can create your own custom cache store by simply extending `ActiveSupport::Cache::Store` and implementing the appropriate methods. In this way, you can swap in any number of caching technologies into your Rails application. -To use a custom cache store, simple set the cache store to a new instance of the class. +To use a custom cache store, simply set the cache store to a new instance of the class. ```ruby config.cache_store = MyCacheStore.new @@ -311,7 +311,7 @@ config.cache_store = MyCacheStore.new ### Cache Keys -The keys used in a cache can be any object that responds to either `:cache_key` or to `:to_param`. You can implement the `:cache_key` method on your classes if you need to generate custom keys. Active Record will generate keys based on the class name and record id. +The keys used in a cache can be any object that responds to either `:cache_key` or `:to_param`. You can implement the `:cache_key` method on your classes if you need to generate custom keys. Active Record will generate keys based on the class name and record id. You can use Hashes and Arrays of values as cache keys. @@ -353,7 +353,7 @@ class ProductsController < ApplicationController end ``` -Instead of an options hash, you can also simply pass in a model, Rails will use the `updated_at` and `cache_key` methods for setting `last_modified` and `etag`: +Instead of an options hash, you can also simply pass in a model. Rails will use the `updated_at` and `cache_key` methods for setting `last_modified` and `etag`: ```ruby class ProductsController < ApplicationController diff --git a/guides/source/configuring.md b/guides/source/configuring.md index aa66376d5d..4da369be5e 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -114,7 +114,7 @@ numbers. New applications filter out passwords by adding the following `config.f defaults to `:debug` for all environments. The available log levels are: `:debug`, `:info`, `:warn`, `:error`, `:fatal`, and `:unknown`. -* `config.log_tags` accepts a list of methods that the `request` object responds to. This makes it easy to tag log lines with debug information like subdomain and request id - both very helpful in debugging multi-user production applications. +* `config.log_tags` accepts a list of: methods that the `request` object responds to, a `Proc` that accepts the `request` object, or something that responds to `to_s`. This makes it easy to tag log lines with debug information like subdomain and request id - both very helpful in debugging multi-user production applications. * `config.logger` accepts a logger conforming to the interface of Log4r or the default Ruby `Logger` class. Defaults to an instance of `ActiveSupport::Logger`. @@ -524,7 +524,7 @@ There are a few configuration options available in Active Support: * `config.active_support.bare` enables or disables the loading of `active_support/all` when booting Rails. Defaults to `nil`, which means `active_support/all` is loaded. -* `config.active_support.test_order` sets the order that test cases are executed. Possible values are `:sorted` and `:random`. Currently defaults to `:sorted`. In Rails 5.0, the default will be changed to `:random` instead. +* `config.active_support.test_order` sets the order that test cases are executed. Possible values are `:random` and `:sorted`. This option is set to `:random` in `config/environments/test.rb` in newly-generated applications. If you have an application that does not specify a `test_order`, it will default to `:sorted`, *until* Rails 5.0, when the default will become `:random`. * `config.active_support.escape_html_entities_in_json` enables or disables the escaping of HTML entities in JSON serialization. Defaults to `false`. diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md index 3d5f8906ca..244add0aee 100644 --- a/guides/source/contributing_to_ruby_on_rails.md +++ b/guides/source/contributing_to_ruby_on_rails.md @@ -532,7 +532,7 @@ pull request". The Rails core team will be notified about your submission. Most pull requests will go through a few iterations before they get merged. Different contributors will sometimes have different opinions, and often -patches will need revised before they can get merged. +patches will need to be revised before they can get merged. Some contributors to Rails have email notifications from GitHub turned on, but others do not. Furthermore, (almost) everyone who works on Rails is a diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md index c6863f68e6..ec3ac62b8c 100644 --- a/guides/source/debugging_rails_applications.md +++ b/guides/source/debugging_rails_applications.md @@ -17,7 +17,7 @@ After reading this guide, you will know: View Helpers for Debugging -------------------------- -One common task is to inspect the contents of a variable. In Rails, you can do this with three methods: +One common task is to inspect the contents of a variable. Rails provides three different ways to do this: * `debug` * `to_yaml` @@ -54,7 +54,7 @@ Title: Rails debugging guide ### `to_yaml` -Displaying an instance variable, or any other object or method, in YAML format can be achieved this way: +Alternatively, calling `to_yaml` on any object converts it to YAML. You can pass this converted object into the `simple_format` helper method to format the output. This is how `debug` does its magic. ```html+erb <%= simple_format @article.to_yaml %> @@ -64,9 +64,7 @@ Displaying an instance variable, or any other object or method, in YAML format c </p> ``` -The `to_yaml` method converts the method to YAML format leaving it more readable, and then the `simple_format` helper is used to render each line as in the console. This is how `debug` method does its magic. - -As a result of this, you will have something like this in your view: +The above code will render something like this: ```yaml --- !ruby/object Article @@ -94,7 +92,7 @@ Another useful method for displaying object values is `inspect`, especially when </p> ``` -Will be rendered as follows: +Will render: ``` [1, 2, 3, 4, 5] @@ -109,9 +107,9 @@ It can also be useful to save information to log files at runtime. Rails maintai ### What is the Logger? -Rails makes use of the `ActiveSupport::Logger` class to write log information. You can also substitute another logger such as `Log4r` if you wish. +Rails makes use of the `ActiveSupport::Logger` class to write log information. Other loggers, such as `Log4r`, may also be substituted. -You can specify an alternative logger in your `environment.rb` or any environment file: +You can specify an alternative logger in `environment.rb` or any other environment file, for example: ```ruby Rails.logger = Logger.new(STDOUT) @@ -130,7 +128,7 @@ TIP: By default, each log is created under `Rails.root/log/` and the log file is ### Log Levels When something is logged, it's printed into the corresponding log if the log -level of the message is equal or higher than the configured log level. If you +level of the message is equal to or higher than the configured log level. If you want to know the current log level, you can call the `Rails.logger.level` method. @@ -143,7 +141,7 @@ config.log_level = :warn # In any environment initializer, or Rails.logger.level = 0 # at any time ``` -This is useful when you want to log under development or staging, but you don't want to flood your production log with unnecessary information. +This is useful when you want to log under development or staging without flooding your production log with unnecessary information. TIP: The default Rails log level is `debug` in all environments. @@ -207,7 +205,7 @@ Adding extra logging like this makes it easy to search for unexpected or unusual When running multi-user, multi-account applications, it's often useful to be able to filter the logs using some custom rules. `TaggedLogging` -in Active Support helps in doing exactly that by stamping log lines with subdomains, request ids, and anything else to aid debugging such applications. +in Active Support helps you do exactly that by stamping log lines with subdomains, request ids, and anything else to aid debugging such applications. ```ruby logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)) @@ -217,34 +215,33 @@ logger.tagged("BCX") { logger.tagged("Jason") { logger.info "Stuff" } } # Logs " ``` ### Impact of Logs on Performance -Logging will always have a small impact on performance of your rails app, - particularly when logging to disk. However, there are a few subtleties: +Logging will always have a small impact on the performance of your Rails app, + particularly when logging to disk. Additionally, there are a few subtleties: Using the `:debug` level will have a greater performance penalty than `:fatal`, as a far greater number of strings are being evaluated and written to the log output (e.g. disk). -Another potential pitfall is that if you have many calls to `Logger` like this - in your code: +Another potential pitfall is too many calls to `Logger` in your code: ```ruby logger.debug "Person attributes hash: #{@person.attributes.inspect}" ``` -In the above example, There will be a performance impact even if the allowed +In the above example, there will be a performance impact even if the allowed output level doesn't include debug. The reason is that Ruby has to evaluate these strings, which includes instantiating the somewhat heavy `String` object -and interpolating the variables, and which takes time. +and interpolating the variables. Therefore, it's recommended to pass blocks to the logger methods, as these are -only evaluated if the output level is the same or included in the allowed level +only evaluated if the output level is the same as — or included in — the allowed level (i.e. lazy loading). The same code rewritten would be: ```ruby logger.debug {"Person attributes hash: #{@person.attributes.inspect}"} ``` -The contents of the block, and therefore the string interpolation, is only -evaluated if debug is enabled. This performance savings is only really +The contents of the block, and therefore the string interpolation, are only +evaluated if debug is enabled. This performance savings are only really noticeable with large amounts of logging, but it's a good practice to employ. Debugging with the `byebug` gem @@ -258,8 +255,7 @@ is your best companion. The debugger can also help you if you want to learn about the Rails source code but don't know where to start. Just debug any request to your application and -use this guide to learn how to move from the code you have written deeper into -Rails code. +use this guide to learn how to move from the code you have written into the underlying Rails code. ### Setup @@ -290,7 +286,7 @@ As soon as your application calls the `byebug` method, the debugger will be started in a debugger shell inside the terminal window where you launched your application server, and you will be placed at the debugger's prompt `(byebug)`. Before the prompt, the code around the line that is about to be run will be -displayed and the current line will be marked by '=>'. Like this: +displayed and the current line will be marked by '=>', like this: ``` [1, 10] in /PathTo/project/app/controllers/articles_controller.rb @@ -344,7 +340,7 @@ Processing by ArticlesController#index as HTML (byebug) ``` -Now it's time to explore and dig into your application. A good place to start is +Now it's time to explore your application. A good place to start is by asking the debugger for help. Type: `help` ``` @@ -365,9 +361,9 @@ continue edit frame kill pp reload skip undisplay TIP: To view the help menu for any command use `help <command-name>` at the debugger prompt. For example: _`help list`_. You can abbreviate any debugging command by supplying just enough letters to distinguish them from other -commands, so you can also use `l` for the `list` command, for example. +commands. For example, you can use `l` for the `list` command. -To see the previous ten lines you should type `list-` (or `l-`) +To see the previous ten lines you should type `list-` (or `l-`). ``` (byebug) l- @@ -386,7 +382,7 @@ To see the previous ten lines you should type `list-` (or `l-`) ``` -This way you can move inside the file, being able to see the code above and over +This way you can move inside the file and see the code above the line where you added the `byebug` call. Finally, to see where you are in the code again you can type `list=` @@ -416,8 +412,7 @@ contexts as you go through the different parts of the stack. The debugger creates a context when a stopping point or an event is reached. The context has information about the suspended program which enables the debugger to inspect the frame stack, evaluate variables from the perspective of the -debugged program, and contains information about the place where the debugged -program is stopped. +debugged program, and know the place where the debugged program is stopped. At any time you can call the `backtrace` command (or its alias `where`) to print the backtrace of the application. This can be very helpful to know how you got @@ -481,9 +476,8 @@ character and the number indicates the current thread of execution. * `thread resume _n_` resumes thread _n_. * `thread switch _n_` switches the current thread context to _n_. -This command is very helpful, among other occasions, when you are debugging -concurrent threads and need to verify that there are no race conditions in your -code. +This command is very helpful when you are debugging concurrent threads and need +to verify that there are no race conditions in your code. ### Inspecting Variables @@ -545,11 +539,11 @@ Now `@articles` is included in the instance variables, because the line defining was executed. TIP: You can also step into **irb** mode with the command `irb` (of course!). -This way an irb session will be started within the context you invoked it. But +This will start an irb session within the context you invoked it. But be warned: this is an experimental feature. The `var` method is the most convenient way to show variables and their values. -Let's let `byebug` help us with it. +Let's have `byebug` help us with it. ``` (byebug) help var @@ -561,7 +555,7 @@ v[ar] l[ocal] show local variables ``` This is a great way to inspect the values of the current context variables. For -example, to check that we have no local variables currently defined. +example, to check that we have no local variables currently defined: ``` (byebug) var local @@ -592,14 +586,14 @@ tracking the values of a variable while the execution goes on. 1: @articles = nil ``` -The variables inside the displaying list will be printed with their values after +The variables inside the displayed list will be printed with their values after you move in the stack. To stop displaying a variable use `undisplay _n_` where _n_ is the variable number (1 in the last example). ### Step by Step Now you should know where you are in the running trace and be able to print the -available variables. But lets continue and move on with the application +available variables. But let's continue and move on with the application execution. Use `step` (abbreviated `s`) to continue running your program until the next @@ -656,8 +650,8 @@ Next went up a frame because previous frame finished (byebug) ``` -If we use `step` in the same situation, we will literally go the next ruby -instruction to be executed. In this case, the activesupport's `week` method. +If we use `step` in the same situation, we will literally go to the next Ruby +instruction to be executed. In this case, Active Support's `week` method. ``` (byebug) step @@ -677,8 +671,7 @@ instruction to be executed. In this case, the activesupport's `week` method. (byebug) ``` -This is one of the best ways to find bugs in your code, or perhaps in Ruby on -Rails. +This is one of the best ways to find bugs in your code. ### Breakpoints @@ -814,7 +807,7 @@ controller. The console would be rendered next to your HTML content. ### Console -Inside any controller action or view, you can then invoke the console by +Inside any controller action or view, you can invoke the console by calling the `console` method. For example, in a controller: @@ -840,7 +833,7 @@ This will render a console inside your view. You don't need to care about the location of the `console` call; it won't be rendered on the spot of its invocation but next to your HTML content. -The console executes pure Ruby code. You can define and instantiate +The console executes pure Ruby code: You can define and instantiate custom classes, create new models and inspect variables. NOTE: Only one console can be rendered per request. Otherwise `web-console` @@ -865,7 +858,7 @@ to use it in production. Debugging Memory Leaks ---------------------- -A Ruby application (on Rails or not), can leak memory - either in the Ruby code +A Ruby application (on Rails or not), can leak memory — either in the Ruby code or at the C code level. In this section, you will learn how to find and fix such leaks by using tool @@ -896,7 +889,7 @@ footnotes that give request information and link back to your source via TextMate. * [Query Trace](https://github.com/ruckus/active-record-query-trace/tree/master) Adds query origin tracing to your logs. -* [Query Reviewer](https://github.com/nesquena/query_reviewer) This rails plugin +* [Query Reviewer](https://github.com/nesquena/query_reviewer) This Rails plugin not only runs "EXPLAIN" before each of your select queries in development, but provides a small DIV in the rendered output of each page with the summary of warnings for each query that it analyzed. @@ -908,7 +901,7 @@ standard Rails error page with a new one containing more contextual information, like source code and variable inspection. * [RailsPanel](https://github.com/dejan/rails_panel) Chrome extension for Rails development that will end your tailing of development.log. Have all information -about your Rails app requests in the browser - in the Developer Tools panel. +about your Rails app requests in the browser — in the Developer Tools panel. Provides insight to db/rendering/total times, parameter list, rendered views and more. diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index 684a53e472..31168ff45e 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -1240,10 +1240,7 @@ article we want to show the form back to the user. We reuse the `article_params` method that we defined earlier for the create action. -TIP: You don't need to pass all attributes to `update`. For -example, if you'd call `@article.update(title: 'A new title')` -Rails would only update the `title` attribute, leaving all other -attributes untouched. +TIP: It is not necessary to pass all the attributes to `update`. For example, if `@article.update(title: 'A new title')` were called, Rails would only update the `title` attribute, leaving all other attributes untouched. Finally, we want to show a link to the `edit` action in the list of all the articles, so let's add that now to `app/views/articles/index.html.erb` to make diff --git a/guides/source/i18n.md b/guides/source/i18n.md index 348c60a9d8..51eaf4ba5a 100644 --- a/guides/source/i18n.md +++ b/guides/source/i18n.md @@ -40,7 +40,7 @@ Internationalization is a complex problem. Natural languages differ in so many w * providing support for English and similar languages out of the box * making it easy to customize and extend everything for other languages -As part of this solution, **every static string in the Rails framework** - e.g. Active Record validation messages, time and date formats - **has been internationalized**, so _localization_ of a Rails application means "over-riding" these defaults. +As part of this solution, **every static string in the Rails framework** - e.g. Active Record validation messages, time and date formats - **has been internationalized**. _Localization_ of a Rails application means defining translated values for these strings in desired languages. ### The Overall Architecture of the Library @@ -84,13 +84,13 @@ So, let's internationalize a simple Rails application from the ground up in the Setup the Rails Application for Internationalization ---------------------------------------------------- -There are just a few simple steps to get up and running with I18n support for your application. +There are a few steps to get up and running with I18n support for a Rails application. ### Configure the I18n Module -Following the _convention over configuration_ philosophy, Rails will set up your application with reasonable defaults. If you need different settings, you can overwrite them easily. +Following the _convention over configuration_ philosophy, Rails I18n provides reasonable default translation strings. When different translation strings are needed, they can be overridden. -Rails adds all `.rb` and `.yml` files from the `config/locales` directory to your **translations load path**, automatically. +Rails adds all `.rb` and `.yml` files from the `config/locales` directory to the **translations load path**, automatically. The default `en.yml` locale in this directory contains a sample pair of translation strings: @@ -101,15 +101,15 @@ en: This means, that in the `:en` locale, the key _hello_ will map to the _Hello world_ string. Every string inside Rails is internationalized in this way, see for instance Active Model validation messages in the [`activemodel/lib/active_model/locale/en.yml`](https://github.com/rails/rails/blob/master/activemodel/lib/active_model/locale/en.yml) file or time and date formats in the [`activesupport/lib/active_support/locale/en.yml`](https://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml) file. You can use YAML or standard Ruby Hashes to store translations in the default (Simple) backend. -The I18n library will use **English** as a **default locale**, i.e. if you don't set a different locale, `:en` will be used for looking up translations. +The I18n library will use **English** as a **default locale**, i.e. if a different locale is not set, `:en` will be used for looking up translations. NOTE: The i18n library takes a **pragmatic approach** to locale keys (after [some discussion](http://groups.google.com/group/rails-i18n/browse_thread/thread/14dede2c7dbe9470/80eec34395f64f3c?hl=en)), including only the _locale_ ("language") part, like `:en`, `:pl`, not the _region_ part, like `:en-US` or `:en-GB`, which are traditionally used for separating "languages" and "regional setting" or "dialects". Many international applications use only the "language" element of a locale such as `:cs`, `:th` or `:es` (for Czech, Thai and Spanish). However, there are also regional differences within different language groups that may be important. For instance, in the `:en-US` locale you would have $ as a currency symbol, while in `:en-GB`, you would have £. Nothing stops you from separating regional and other settings in this way: you just have to provide full "English - United Kingdom" locale in a `:en-GB` dictionary. Few gems such as [Globalize3](https://github.com/globalize/globalize) may help you implement it. -The **translations load path** (`I18n.load_path`) is just a Ruby Array of paths to your translation files that will be loaded automatically and available in your application. You can pick whatever directory and translation file naming scheme makes sense for you. +The **translations load path** (`I18n.load_path`) is an array of paths to files that will be loaded automatically. Configuring this path allows for customization of translations directory structure and file naming scheme. -NOTE: The backend will lazy-load these translations when a translation is looked up for the first time. This makes it possible to just swap the backend with something else even after translations have already been announced. +NOTE: The backend lazy-loads these translations when a translation is looked up for the first time. This backend can be swapped with something else even after translations have already been announced. -The default `application.rb` file has instructions on how to add locales from another directory and how to set a different default locale. Just uncomment and edit the specific lines. +The default `application.rb` file has instructions on how to add locales from another directory and how to set a different default locale. ```ruby # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. @@ -117,31 +117,25 @@ The default `application.rb` file has instructions on how to add locales from an # config.i18n.default_locale = :de ``` -### Optional: Custom I18n Configuration Setup - -For the sake of completeness, let's mention that if you do not want to use the `application.rb` file for some reason, you can always wire up things manually, too. - -To tell the I18n library where it can find your custom translation files you can specify the load path anywhere in your application - just make sure it gets run before any translations are actually looked up. You might also want to change the default locale. The simplest thing possible is to put the following into an initializer: +The load path must be specified before any translations are looked up. To change the default locale from an initializer instead of `application.rb`: ```ruby -# in config/initializers/locale.rb +# config/initializers/locale.rb -# tell the I18n library where to find your translations +# Where the I18n library should search for translation files I18n.load_path += Dir[Rails.root.join('lib', 'locale', '*.{rb,yml}')] -# set default locale to something other than :en +# Set default locale to something other than :en I18n.default_locale = :pt ``` -### Setting and Passing the Locale +### Managing the Locale across Requests -If you want to translate your Rails application to a **single language other than English** (the default locale), you can set I18n.default_locale to your locale in `application.rb` or an initializer as shown above, and it will persist through the requests. +The default locale is used for all translations unless `I18n.locale` is explicitly set. -However, you would probably like to **provide support for more locales** in your application. In such case, you need to set and pass the locale between requests. +A localized application will likely need to provide support for multiple locales. To accomplish this, the locale should be set at the beginning of each request so that all strings are translated using the desired locale during the lifetime of that request. -WARNING: You may be tempted to store the chosen locale in a _session_ or a *cookie*. However, **do not do this**. The locale should be transparent and a part of the URL. This way you won't break people's basic assumptions about the web itself: if you send a URL to a friend, they should see the same page and content as you. A fancy word for this would be that you're being [*RESTful*](http://en.wikipedia.org/wiki/Representational_State_Transfer). Read more about the RESTful approach in [Stefan Tilkov's articles](http://www.infoq.com/articles/rest-introduction). Sometimes there are exceptions to this rule and those are discussed below. - -The _setting part_ is easy. You can set the locale in a `before_action` in the `ApplicationController` like this: +The locale can be set in a `before_action` in the `ApplicationController`: ```ruby before_action :set_locale @@ -151,11 +145,11 @@ def set_locale end ``` -This requires you to pass the locale as a URL query parameter as in `http://example.com/books?locale=pt`. (This is, for example, Google's approach.) So `http://localhost:3000?locale=pt` will load the Portuguese localization, whereas `http://localhost:3000?locale=de` would load the German localization, and so on. You may skip the next section and head over to the **Internationalize your application** section, if you want to try things out by manually placing the locale in the URL and reloading the page. +This example illustrates this using a URL query parameter to set the locale (e.g. `http://example.com/books?locale=pt`). With this approach, `http://localhost:3000?locale=pt` renders the Portuguese localization, while `http://localhost:3000?locale=de` loads a German localization. -Of course, you probably don't want to manually include the locale in every URL all over your application, or want the URLs look differently, e.g. the usual `http://example.com/pt/books` versus `http://example.com/en/books`. Let's discuss the different options you have. +The locale can be set using one of many different approaches. -### Setting the Locale from the Domain Name +#### Setting the Locale from the Domain Name One option you have is to set the locale from the domain name where your application runs. For example, we want `www.example.com` to load the English (or default) locale, and `www.example.es` to load the Spanish locale. Thus the _top-level domain name_ is used for locale setting. This has several advantages: @@ -208,7 +202,7 @@ assuming you would set `APP_CONFIG[:deutsch_website_url]` to some value like `ht This solution has aforementioned advantages, however, you may not be able or may not want to provide different localizations ("language versions") on different domains. The most obvious solution would be to include locale code in the URL params (or request path). -### Setting the Locale from the URL Params +#### Setting the Locale from URL Params The most usual way of setting (and passing) the locale would be to include it in URL params, as we did in the `I18n.locale = params[:locale]` _before_action_ in the first example. We would like to have URLs like `www.example.com/books?locale=ja` or `www.example.com/ja/books` in this case. @@ -266,14 +260,23 @@ Do take special care about the **order of your routes**, so this route declarati NOTE: Have a look at various gems which simplify working with routes: [routing_filter](https://github.com/svenfuchs/routing-filter/tree/master), [rails-translate-routes](https://github.com/francesc/rails-translate-routes), [route_translator](https://github.com/enriclluelles/route_translator). -### Setting the Locale from the Client Supplied Information +#### Setting the Locale from User Preferences + +An application with authenticated users may allow users to set a locale preference through the application's interface. With this approach, a user's selected locale preference is persisted in the database and used to set the locale for authenticated requests by that user. + +```ruby +def set_locale + I18n.locale = current_user.try(:locale) || I18n.default_locale +end +``` -In specific cases, it would make sense to set the locale from client-supplied information, i.e. not from the URL. This information may come for example from the users' preferred language (set in their browser), can be based on the users' geographical location inferred from their IP, or users can provide it simply by choosing the locale in your application interface and saving it to their profile. This approach is more suitable for web-based applications or services, not for websites - see the box about _sessions_, _cookies_ and RESTful architecture above. +#### Choosing an Implied Locale +When an explicit locale has not been set for a request (e.g. via one of the above methods), an application should attempt to infer the desired locale. -#### Using `Accept-Language` +##### Inferring Locale from the Language Header -One source of client supplied information would be an `Accept-Language` HTTP header. People may [set this in their browser](http://www.w3.org/International/questions/qa-lang-priorities) or other clients (such as _curl_). +The `Accept-Language` HTTP header indicates the preferred language for request's response. Browsers [set this header value based on the user's language preference settings](http://www.w3.org/International/questions/qa-lang-priorities), making it a good first choice when inferring a locale. A trivial implementation of using an `Accept-Language` header would be: @@ -290,24 +293,27 @@ private end ``` -Of course, in a production environment you would need much more robust code, and could use a gem such as Iain Hecker's [http_accept_language](https://github.com/iain/http_accept_language/tree/master) or even Rack middleware such as Ryan Tomayko's [locale](https://github.com/rack/rack-contrib/blob/master/lib/rack/contrib/locale.rb). -#### Using GeoIP (or Similar) Database +In practice, more robust code is necessary to do this reliably. Iain Hecker's [http_accept_language](https://github.com/iain/http_accept_language/tree/master) library or Ryan Tomayko's [locale](https://github.com/rack/rack-contrib/blob/master/lib/rack/contrib/locale.rb) Rack middleware provide solutions to this problem. -Another way of choosing the locale from client information would be to use a database for mapping the client IP to the region, such as [GeoIP Lite Country](http://www.maxmind.com/app/geolitecountry). The mechanics of the code would be very similar to the code above - you would need to query the database for the user's IP, and look up your preferred locale for the country/region/city returned. +##### Inferring the Locale from IP Geolocation -#### User Profile +The IP address of the client making the request can be used to infer the client's region and thus their locale. Services such as [GeoIP Lite Country](http://www.maxmind.com/app/geolitecountry) or gems like [geocoder](https://github.com/alexreisner/geocoder) can be used to implement this approach. -You can also provide users of your application with means to set (and possibly over-ride) the locale in your application interface, as well. Again, mechanics for this approach would be very similar to the code above - you'd probably let users choose a locale from a dropdown list and save it to their profile in the database. Then you'd set the locale to this value. +In general, this approach is far less reliable than using the language header and is not recommended for most web applications. -Internationalizing your Application +#### Storing the Locale from the Session or Cookies + +WARNING: You may be tempted to store the chosen locale in a _session_ or a *cookie*. However, **do not do this**. The locale should be transparent and a part of the URL. This way you won't break people's basic assumptions about the web itself: if you send a URL to a friend, they should see the same page and content as you. A fancy word for this would be that you're being [*RESTful*](http://en.wikipedia.org/wiki/Representational_State_Transfer). Read more about the RESTful approach in [Stefan Tilkov's articles](http://www.infoq.com/articles/rest-introduction). Sometimes there are exceptions to this rule and those are discussed below. + +Internationalization and Localization ----------------------------------- -OK! Now you've initialized I18n support for your Ruby on Rails application and told it which locale to use and how to preserve it between requests. With that in place, you're now ready for the really interesting stuff. +OK! Now you've initialized I18n support for your Ruby on Rails application and told it which locale to use and how to preserve it between requests. -Let's _internationalize_ our application, i.e. abstract every locale-specific parts, and then _localize_ it, i.e. provide necessary translations for these abstracts. +Next we need to _internationalize_ our application by abstracting every locale-specific element. Finally, we need to _localize_ it by providing necessary translations for these abstracts. -You most probably have something like this in one of your applications: +Given the following example: ```ruby # config/routes.rb @@ -344,9 +350,9 @@ end ![rails i18n demo untranslated](images/i18n/demo_untranslated.png) -### Adding Translations +### Abstracting Localized Code -Obviously there are **two strings that are localized to English**. In order to internationalize this code, **replace these strings** with calls to Rails' `#t` helper with a key that makes sense for the translation: +There are two strings in our code that are in English and that users will be rendered in our response ("Hello Flash" and "Hello World"). In order to internationalize this code, these strings need to be replaced by calls to Rails' `#t` helper with an appropriate key for each string: ```ruby # app/controllers/home_controller.rb @@ -363,13 +369,15 @@ end <p><%= flash[:notice] %></p> ``` -When you now render this view, it will show an error message which tells you that the translations for the keys `:hello_world` and `:hello_flash` are missing. +Now, when this view is rendered, it will show an error message which tells you that the translations for the keys `:hello_world` and `:hello_flash` are missing. ![rails i18n demo translation missing](images/i18n/demo_translation_missing.png) NOTE: Rails adds a `t` (`translate`) helper method to your views so that you do not need to spell out `I18n.t` all the time. Additionally this helper will catch missing translations and wrap the resulting error message into a `<span class="translation_missing">`. -So let's add the missing translations into the dictionary files (i.e. do the "localization" part): +### Providing Translations for Internationalized Strings + +Add the missing translations into the translation dictionary files: ```yaml # config/locales/en.yml @@ -383,11 +391,11 @@ pirate: hello_flash: Ahoy Flash ``` -There you go. Because you haven't changed the default_locale, I18n will use English. Your application now shows: +Because the `default_locale` hasn't changed, translations use the `:en` locale and the response renders the english strings: ![rails i18n demo translated to English](images/i18n/demo_translated_en.png) -And when you change the URL to pass the pirate locale (`http://localhost:3000?locale=pirate`), you'll get: +If the locale is set via the URL to the pirate locale (`http://localhost:3000?locale=pirate`), the response renders the pirate strings: ![rails i18n demo translated to pirate](images/i18n/demo_translated_pirate.png) @@ -395,21 +403,64 @@ NOTE: You need to restart the server when you add new locale files. You may use YAML (`.yml`) or plain Ruby (`.rb`) files for storing your translations in SimpleStore. YAML is the preferred option among Rails developers. However, it has one big disadvantage. YAML is very sensitive to whitespace and special characters, so the application may not load your dictionary properly. Ruby files will crash your application on first request, so you may easily find what's wrong. (If you encounter any "weird issues" with YAML dictionaries, try putting the relevant portion of your dictionary into a Ruby file.) -### Passing variables to translations +### Passing Variables to Translations + +One key consideration for successfully internationalizing an application is to +avoid making incorrect assumptions about grammar rules when abstracting localized +code. Grammar rules that seem fundamental in one locale may not hold true in +another one. -You can use variables in the translation messages and pass their values from the view. +Improper abstraction is shown in the following example, where assumptions are +made about the ordering of the different parts of the translation. Note that Rails +provides a `number_to_currency` helper to handle the following case. ```erb -# app/views/home/index.html.erb -<%=t 'greet_username', user: "Bill", message: "Goodbye" %> +# app/views/products/show.html.erb +<%= "#{t('currency')}#{@product.price}" %> +``` + +```yaml +# config/locales/en.yml +en: + currency: "$" + +# config/locales/es.yml +es: + currency: "€" +``` + +If the product's price is 10 then the proper translation for Spanish is "10 €" +instead of "€10" but the abstraction cannot give it. + +To create proper abstraction, the I18n gem ships with a feature called variable +interpolation that allows you to use variables in translation definitions and +pass the values for these variables to the translation method. + +Proper abstraction is shown in the following example: + +```erb +# app/views/products/show.html.erb +<%= t('product_price', price: @product.price) %> ``` ```yaml # config/locales/en.yml en: - greet_username: "%{message}, %{user}!" + product_price: "$%{price}" + +# config/locales/es.yml +es: + product_price: "%{price} €" ``` +All grammatical and punctuation decisions are made in the definition itself, so +the abstraction can give a proper translation. + +NOTE: The `default` and `scope` keywords are reserved and can't be used as +variable names. If used, an `I18n::ReservedInterpolationKey` exception is raised. +If a translation expects an interpolation variable, but this has not been passed +to `#translate`, an `I18n::MissingInterpolationArgument` exception is raised. + ### Adding Date/Time Formats OK! Now let's add a timestamp to the view, so we can demo the **date/time localization** feature as well. To localize the time format you pass the Time object to `I18n.l` or (preferably) use Rails' `#l` helper. You can pick a format by passing the `:format` option - by default the `:default` format is used. @@ -610,20 +661,6 @@ class BooksController < ApplicationController end ``` -### Interpolation - -In many cases you want to abstract your translations so that **variables can be interpolated into the translation**. For this reason the I18n API provides an interpolation feature. - -All options besides `:default` and `:scope` that are passed to `#translate` will be interpolated to the translation: - -```ruby -I18n.backend.store_translations :en, thanks: 'Thanks %{name}!' -I18n.translate :thanks, name: 'Jeremy' -# => 'Thanks Jeremy!' -``` - -If a translation uses `:default` or `:scope` as an interpolation variable, an `I18n::ReservedInterpolationKey` exception is raised. If a translation expects an interpolation variable, but this has not been passed to `#translate`, an `I18n::MissingInterpolationArgument` exception is raised. - ### Pluralization In English there are only one singular and one plural form for a given string, e.g. "1 message" and "2 messages". Other languages ([Arabic](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html#ar), [Japanese](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html#ja), [Russian](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html#ru) and many more) have different grammars that have additional or fewer [plural forms](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html). Thus, the I18n API provides a flexible pluralization feature. diff --git a/guides/source/ruby_on_rails_guides_guidelines.md b/guides/source/ruby_on_rails_guides_guidelines.md index 1323742488..50866350f8 100644 --- a/guides/source/ruby_on_rails_guides_guidelines.md +++ b/guides/source/ruby_on_rails_guides_guidelines.md @@ -64,7 +64,9 @@ The guides and the API should be coherent and consistent where appropriate. In p HTML Guides ----------- -Before generating the guides, make sure that you have the latest version of Bundler installed on your system. As of this writing, you must install Bundler 1.3.5 on your device. +Before generating the guides, make sure that you have the latest version of +Bundler installed on your system. As of this writing, you must install Bundler +1.3.5 or later on your device. To install the latest version of Bundler, run `gem install bundler`. @@ -82,6 +84,8 @@ or bundle exec rake guides:generate:html ``` +Resulting HTML files can be found in the `./output` directory. + To process `my_guide.md` and nothing else use the `ONLY` environment variable: ``` diff --git a/guides/source/testing.md b/guides/source/testing.md index f12daf0dbc..cc469f4dae 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -407,9 +407,49 @@ This test should now pass. By now you've caught a glimpse of some of the assertions that are available. Assertions are the worker bees of testing. They are the ones that actually perform the checks to ensure that things are going as planned. -There are a bunch of different types of assertions you can use that come with [`Minitest`](https://github.com/seattlerb/minitest), the default testing library used by Rails. - -For a list of all available assertions please check the [Minitest API documentation](http://docs.seattlerb.org/minitest/), specifically [`Minitest::Assertions`](http://docs.seattlerb.org/minitest/Minitest/Assertions.html) +Here's an extract of the assertions you can use with +[`Minitest`](https://github.com/seattlerb/minitest), the default testing library +used by Rails. The `[msg]` parameter is an optional string message you can +specify to make your test failure messages clearer. It's not required. + +| Assertion | Purpose | +| ---------------------------------------------------------------- | ------- | +| `assert( test, [msg] )` | Ensures that `test` is true.| +| `assert_not( test, [msg] )` | Ensures that `test` is false.| +| `assert_equal( expected, actual, [msg] )` | Ensures that `expected == actual` is true.| +| `assert_not_equal( expected, actual, [msg] )` | Ensures that `expected != actual` is true.| +| `assert_same( expected, actual, [msg] )` | Ensures that `expected.equal?(actual)` is true.| +| `assert_not_same( expected, actual, [msg] )` | Ensures that `expected.equal?(actual)` is false.| +| `assert_nil( obj, [msg] )` | Ensures that `obj.nil?` is true.| +| `assert_not_nil( obj, [msg] )` | Ensures that `obj.nil?` is false.| +| `assert_empty( obj, [msg] )` | Ensures that `obj` is `empty?`.| +| `assert_not_empty( obj, [msg] )` | Ensures that `obj` is not `empty?`.| +| `assert_match( regexp, string, [msg] )` | Ensures that a string matches the regular expression.| +| `assert_no_match( regexp, string, [msg] )` | Ensures that a string doesn't match the regular expression.| +| `assert_includes( collection, obj, [msg] )` | Ensures that `obj` is in `collection`.| +| `assert_not_includes( collection, obj, [msg] )` | Ensures that `obj` is not in `collection`.| +| `assert_in_delta( expecting, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are within `delta` of each other.| +| `assert_not_in_delta( expecting, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are not within `delta` of each other.| +| `assert_throws( symbol, [msg] ) { block }` | Ensures that the given block throws the symbol.| +| `assert_raises( exception1, exception2, ... ) { block }` | Ensures that the given block raises one of the given exceptions.| +| `assert_nothing_raised( exception1, exception2, ... ) { block }` | Ensures that the given block doesn't raise one of the given exceptions.| +| `assert_instance_of( class, obj, [msg] )` | Ensures that `obj` is an instance of `class`.| +| `assert_not_instance_of( class, obj, [msg] )` | Ensures that `obj` is not an instance of `class`.| +| `assert_kind_of( class, obj, [msg] )` | Ensures that `obj` is or descends from `class`.| +| `assert_not_kind_of( class, obj, [msg] )` | Ensures that `obj` is not an instance of `class` and is not descending from it.| +| `assert_respond_to( obj, symbol, [msg] )` | Ensures that `obj` responds to `symbol`.| +| `assert_not_respond_to( obj, symbol, [msg] )` | Ensures that `obj` does not respond to `symbol`.| +| `assert_operator( obj1, operator, [obj2], [msg] )` | Ensures that `obj1.operator(obj2)` is true.| +| `assert_not_operator( obj1, operator, [obj2], [msg] )` | Ensures that `obj1.operator(obj2)` is false.| +| `assert_predicate ( obj, predicate, [msg] )` | Ensures that `obj.predicate` is true, e.g. `assert_predicate str, :empty?`| +| `assert_not_predicate ( obj, predicate, [msg] )` | Ensures that `obj.predicate` is false, e.g. `assert_not_predicate str, :empty?`| +| `assert_send( array, [msg] )` | Ensures that executing the method listed in `array[1]` on the object in `array[0]` with the parameters of `array[2 and up]` is true. This one is weird eh?| +| `flunk( [msg] )` | Ensures failure. This is useful to explicitly mark a test that isn't finished yet.| + +The above are a subset of assertions that minitest supports. For an exhaustive & +more up-to-date list, please check +[Minitest API documentation](http://docs.seattlerb.org/minitest/), specifically +[`Minitest::Assertions`](http://docs.seattlerb.org/minitest/Minitest/Assertions.html) Because of the modular nature of the testing framework, it is possible to create your own assertions. In fact, that's exactly what Rails does. It includes some specialized assertions to make your life easier. diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index 0aa2152d56..49834fa8a2 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -883,6 +883,20 @@ this gem such as `whitelist_attributes` or `mass_assignment_sanitizer` options. * To re-enable the old finders, you can use the [activerecord-deprecated_finders gem](https://github.com/rails/activerecord-deprecated_finders). +* Rails 4.0 has changed to default join table for `has_and_belongs_to_many` relations to strip the common prefix off the second table name. Any existing `has_and_belongs_to_many` relationship between models with a common prefix must be specified with the `join_table` option. For example: + +```ruby +CatalogCategory < ActiveRecord::Base + has_and_belongs_to_many :catalog_products, join_table: 'catalog_categories_catalog_products' +end + +CatalogProduct < ActiveRecord::Base + has_and_belongs_to_many :catalog_categories, join_table: 'catalog_categories_catalog_products' +end +``` + +* Note that the the prefix takes scopes into account as well, so relations between `Catalog::Category` and `Catalog::Product` or `Catalog::Category` and `CatalogProduct` need to be updated similarly. + ### Active Resource Rails 4.0 extracted Active Resource to its own gem. If you still need the feature you can add the [Active Resource gem](https://github.com/rails/activeresource) in your Gemfile. |