diff --git a/actionpack/lib/action_controller/caching/sweeping.rb b/actionpack/lib/action_controller/caching/sweeping.rb index 73291ce083..271d5f06b8 100644 --- a/actionpack/lib/action_controller/caching/sweeping.rb +++ b/actionpack/lib/action_controller/caching/sweeping.rb @@ -1,6 +1,6 @@ module ActionController #:nodoc: module Caching - # Sweepers are the terminators of the caching world and responsible for expiring caches when model objects change. + # Sweepers are the terminators of the caching world and responsible for expiring caches when Active Record objects change. # They do this by being half-observers, half-filters and implementing callbacks for both roles. A Sweeper example: # # class ListSweeper < ActionController::Caching::Sweeper diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb index eb6e604851..03046a543a 100644 --- a/activemodel/lib/active_model/validations/validates.rb +++ b/activemodel/lib/active_model/validations/validates.rb @@ -94,10 +94,10 @@ module ActiveModel # validates :token, uniqueness: true, strict: TokenGenerationException # # - # Finally, the options +:if+, +:unless+, +:on+, +:allow_blank+, +:allow_nil+ - # and +:strict+ can be given to one specific validator, as a hash: + # Finally, the options +:if+, +:unless+, +:on+, +:allow_blank+, +:allow_nil+, +:strict+ + # and +:message+ can be given to one specific validator, as a hash: # - # validates :password, presence: { if: :password_required? }, confirmation: true + # validates :password, presence: { if: :password_required?, message: 'is forgotten.' }, confirmation: true def validates(*attributes) defaults = attributes.extract_options!.dup validations = defaults.slice!(*_validates_default_keys) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 7a3d9bfd3e..bf08459b3b 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -494,7 +494,7 @@ module ActiveRecord # # Normally there is only a single ConnectionHandler instance, accessible via # ActiveRecord::Base.connection_handler. Active Record models use this to - # determine that connection pool that they should use. + # determine the connection pool that they should use. class ConnectionHandler def initialize @owner_to_pool = Hash.new { |h,k| h[k] = {} } diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb index 25f528498e..8ea0ea239f 100644 --- a/activerecord/lib/active_record/store.rb +++ b/activerecord/lib/active_record/store.rb @@ -82,7 +82,7 @@ module ActiveRecord attribute end - class IndifferentCoder + class IndifferentCoder # :nodoc: def initialize(coder_or_class_name) @coder = if coder_or_class_name.respond_to?(:load) && coder_or_class_name.respond_to?(:dump) diff --git a/guides/source/4_0_release_notes.textile b/guides/source/4_0_release_notes.textile index 0e03779e0d..fc922b04b7 100644 --- a/guides/source/4_0_release_notes.textile +++ b/guides/source/4_0_release_notes.textile @@ -116,6 +116,20 @@ h3. Action Mailer * Asynchronously send messages via the Rails Queue. +* Delivery Options (such as SMTP Settings) can now be set dynamically per mailer action. + +Delivery options are set via :delivery_method_options key on mail. + + +def welcome_mailer(user,company) + mail to: user.email, + subject: "Welcome!", + delivery_method_options: {user_name: company.smtp_user, + password: company.smtp_password, + address: company.smtp_server} +end + + h3. Action Pack h4. Action Controller diff --git a/guides/source/action_mailer_basics.textile b/guides/source/action_mailer_basics.textile index bca403ae72..5f09b8e410 100644 --- a/guides/source/action_mailer_basics.textile +++ b/guides/source/action_mailer_basics.textile @@ -410,6 +410,21 @@ end The above will send a multipart email with an attachment, properly nested with the top level being multipart/mixed and the first part being a multipart/alternative containing the plain text and HTML email messages. +h5. Sending Emails with Dynamic Delivery Options + +If you wish to override the default delivery options (e.g. SMTP credentials) while delivering emails, you can do this using +delivery_method_options+ in the mailer action. + + +class UserMailer < ActionMailer::Base + def welcome_email(user,company) + @user = user + @url = user_url(@user) + delivery_options = { user_name: company.smtp_user, password: company.smtp_password, address: company.smtp_host } + mail(to: user.email, subject: "Please see the Terms and Conditions attached", delivery_method_options: delivery_options) + end +end + + h3. Receiving Emails Receiving and parsing emails with Action Mailer can be a rather complex endeavor. Before your email reaches your Rails app, you would have had to configure your system to somehow forward emails to your app, which needs to be listening for that. So, to receive emails in your Rails app you'll need to: diff --git a/guides/source/action_view_overview.textile b/guides/source/action_view_overview.textile index 1fd98a5bbe..33ae7f6933 100644 --- a/guides/source/action_view_overview.textile +++ b/guides/source/action_view_overview.textile @@ -1211,7 +1211,7 @@ Sample usage (selecting the associated Authors for an instance of Post, +@post+) collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) -If @post.author_ids is [1], this would return: +If @post.author_ids is [1], this would return: diff --git a/guides/source/active_record_querying.textile b/guides/source/active_record_querying.textile index 80c9260a0d..96ae5b2972 100644 --- a/guides/source/active_record_querying.textile +++ b/guides/source/active_record_querying.textile @@ -266,7 +266,7 @@ SELECT * FROM clients WHERE (clients.id IN (1,10)) WARNING: Model.find(array_of_primary_key) will raise an +ActiveRecord::RecordNotFound+ exception unless a matching record is found for all of the supplied primary keys. -h5. take +h5(#take-n-objects). take Model.take(limit) retrieves the first number of records specified by +limit+ without any explicit ordering: @@ -282,7 +282,7 @@ The SQL equivalent of the above is: SELECT * FROM clients LIMIT 2 -h5. first +h5(#first-n-objects). first Model.first(limit) finds the first number of records specified by +limit+ ordered by primary key: @@ -298,7 +298,7 @@ The SQL equivalent of the above is: SELECT * FROM clients LIMIT 2 -h5. last +h5(#last-n-objects). last Model.last(limit) finds the number of records specified by +limit+ ordered by primary key in descending order: @@ -1144,7 +1144,8 @@ category.posts.created_before(time) h4. Applying a default scope -If we wish for a scope to be applied across all queries to the model we can use the +default_scope+ method within the model itself. +If we wish for a scope to be applied across all queries to the model we can use the ++default_scope+ method within the model itself. class Client < ActiveRecord::Base @@ -1152,15 +1153,29 @@ class Client < ActiveRecord::Base end -When queries are executed on this model, the SQL query will now look something like this: +When queries are executed on this model, the SQL query will now look something like +this: SELECT * FROM clients WHERE removed_at IS NULL +If you need to do more complex things with a default scope, you can alternatively +define it as a class method: + + +class Client < ActiveRecord::Base + def self.default_scope + # Should return an ActiveRecord::Relation. + end +end + + h4. Removing all scoping -If we wish to remove scoping for any reason we can use the +unscoped+ method. This is especially useful if a +default_scope+ is specified in the model and should not be applied for this particular query. +If we wish to remove scoping for any reason we can use the +unscoped+ method. This is +especially useful if a +default_scope+ is specified in the model and should not be +applied for this particular query. Client.unscoped.all @@ -1168,6 +1183,15 @@ Client.unscoped.all This method removes all scoping and will do a normal query on the table. +Note that chaining +unscoped+ with a +scope+ does not work. In these cases, it is +recommended that you use the block form of +unscoped+: + + +Client.unscoped { + Client.created_before(Time.zome.now) +} + + h3. Dynamic Finders For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called +first_name+ on your +Client+ model for example, you get +find_by_first_name+ and +find_all_by_first_name+ for free from Active Record. If you have a +locked+ field on the +Client+ model, you also get +find_by_locked+ and +find_all_by_locked+ methods. @@ -1218,7 +1242,7 @@ Client.find_or_create_by_first_name(:first_name => "Andy", :locked => false) This method still works, but it's encouraged to use +first_or_create+ because it's more explicit on which arguments are used to _find_ the record and which are used to _create_, resulting in less confusion overall. -h4. +first_or_create!+ +h4(#first_or_create_bang). +first_or_create!+ You can also use +first_or_create!+ to raise an exception if the new record is invalid. Validations are not covered on this guide, but let's assume for a moment that you temporarily add diff --git a/guides/source/association_basics.textile b/guides/source/association_basics.textile index d4c0a1ba74..151752eee9 100644 --- a/guides/source/association_basics.textile +++ b/guides/source/association_basics.textile @@ -88,6 +88,8 @@ end !images/belongs_to.png(belongs_to Association Diagram)! +NOTE: +belongs_to+ associations _must_ use the singular term. If you used the pluralized form in the above example for the +customer+ association in the +Order+ model, you would be told that there was an "uninitialized constant Order::Customers". This is because Rails automatically infers the class name from the association name. If the association name is wrongly pluralized, then the inferred class will be wrongly pluralized too. + h4. The +has_one+ Association A +has_one+ association also sets up a one-to-one connection with another model, but with somewhat different semantics (and consequences). This association indicates that each instance of a model contains or possesses one instance of another model. For example, if each supplier in your application has only one account, you'd declare the supplier model like this: diff --git a/guides/source/caching_with_rails.textile b/guides/source/caching_with_rails.textile index 815b2ef9c2..712440da32 100644 --- a/guides/source/caching_with_rails.textile +++ b/guides/source/caching_with_rails.textile @@ -173,7 +173,9 @@ expire_fragment('all_available_products') h4. Sweepers -Cache sweeping is a mechanism which allows you to get around having a ton of +expire_{page,action,fragment}+ calls in your code. It does this by moving all the work required to expire cached content into an +ActionController::Caching::Sweeper+ subclass. This class is an observer and looks for changes to an object via callbacks, and when a change occurs it expires the caches associated with that object in an around or after filter. +Cache sweeping is a mechanism which allows you to get around having a ton of +expire_{page,action,fragment}+ calls in your code. It does this by moving all the work required to expire cached content into an +ActionController::Caching::Sweeper+ subclass. This class is an observer and looks for changes to an Active Record object via callbacks, and when a change occurs it expires the caches associated with that object in an around or after filter. + +TIP: Sweepers rely on the use of Active Record and Active Record Observers. The object you are observing must be an Active Record model. Continuing with our Product controller example, we could rewrite it with a sweeper like this: @@ -353,7 +355,7 @@ Note that the cache will grow until the disk is full unless you periodically cle h4. ActiveSupport::Cache::MemCacheStore -This cache store uses Danga's +memcached+ server to provide a centralized cache for your application. Rails uses the bundled +memcache-client+ gem by default. This is currently the most popular cache store for production websites. It can be used to provide a single, shared cache cluster with very a high performance and redundancy. +This cache store uses Danga's +memcached+ server to provide a centralized cache for your application. Rails uses the bundled +dalli+ gem by default. This is currently the most popular cache store for production websites. It can be used to provide a single, shared cache cluster with very a high performance and redundancy. 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. @@ -439,7 +441,7 @@ class ProductsController < ApplicationController # If the request is stale according to the given timestamp and etag value # (i.e. it needs to be processed again) then execute this block - if stale?(:last_modified => @product.updated_at.utc, :etag => @product) + if stale?(:last_modified => @product.updated_at.utc, :etag => @product.cache_key) respond_to do |wants| # ... normal response processing end @@ -453,6 +455,17 @@ class ProductsController < ApplicationController end +Instead of a 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+: + + +class ProductsController < ApplicationController + def show + @product = Product.find(params[:id]) + respond_with(@product) if stale?(@product) + end +end + + If you don't have any special response processing and are using the default rendering mechanism (i.e. you're not using respond_to or calling render yourself) then you’ve got an easy helper in fresh_when: diff --git a/guides/source/configuring.textile b/guides/source/configuring.textile index 9db375c2ca..dd84754b22 100644 --- a/guides/source/configuring.textile +++ b/guides/source/configuring.textile @@ -388,7 +388,7 @@ And can reference in the view with the following code: <%= render @post %> - + The default setting is +true+, which uses the partial at +/admin/posts/_post.erb+. Setting the value to +false+ would render +/posts/_post.erb+, which is the same behavior as rendering from a non-namespaced controller such as +PostsController+. diff --git a/guides/source/debugging_rails_applications.textile b/guides/source/debugging_rails_applications.textile index 667f2d2140..a5a22a8a8f 100644 --- a/guides/source/debugging_rails_applications.textile +++ b/guides/source/debugging_rails_applications.textile @@ -191,6 +191,17 @@ Completed in 0.01224 (81 reqs/sec) | DB: 0.00044 (3%) | 302 Found [http://localh Adding extra logging like this makes it easy to search for unexpected or unusual behavior in your logs. If you add extra logging, be sure to make sensible use of log levels, to avoid filling your production logs with useless trivia. +h4. Tagged Logging + +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. + + +logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)) +logger.tagged("BCX") { logger.info "Stuff" } # Logs "[BCX] Stuff" +logger.tagged("BCX", "Jason") { logger.info "Stuff" } # Logs "[BCX] [Jason] Stuff" +logger.tagged("BCX") { logger.tagged("Jason") { logger.info "Stuff" } } # Logs "[BCX] [Jason] Stuff" + + h3. Debugging with the +debugger+ gem When your code is behaving in unexpected ways, you can try printing to logs or the console to diagnose the problem. Unfortunately, there are times when this sort of error tracking is not effective in finding the root cause of a problem. When you actually need to journey into your running source code, the debugger is your best companion. diff --git a/guides/source/engines.textile b/guides/source/engines.textile index fe8fcfbb3f..8fcc7823f0 100644 --- a/guides/source/engines.textile +++ b/guides/source/engines.textile @@ -26,7 +26,7 @@ It's important to keep in mind at all times that the application should *always* 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/resolve/refinerycms, a CMS engine. -Finally, engines would not have be 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! +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! h3. Generating an engine @@ -42,9 +42,9 @@ The full list of options for the plugin generator may be seen by typing: $ rails plugin --help -The +--full+ option tells the plugin generator that you want to create an engine (which is a mountable plugin, hence the option name), creating the basic directory structure of an engine by providing things such as the foundations of an +app+ folder, as well a +config/routes.rb+ file. This generator also provides a file at +lib/blorgh/engine.rb+ which is identical in function to an application's +config/application.rb+ file. +The +--full+ option tells the plugin generator that you want to create an engine, creating the basic directory structure of an engine by providing things such as an +app+ directory and a +config/routes.rb+ file. This generator also provides a file at +lib/blorgh/engine.rb+ which is identical in function to a standard Rails application's +config/application.rb+ file. -The +--mountable+ option tells the generator to mount the engine inside the dummy testing application located at +test/dummy+ inside the engine. It does this by placing this line in to the dummy application's +config/routes.rb+ file, located at +test/dummy/config/routes.rb+ inside the engine: +The +--mountable+ option tells the generator to mount the engine inside the dummy testing application located at +test/dummy+. It does this by placing this line into the dummy application's routes file at +test/dummy/config/routes.rb+: mount Blorgh::Engine, :at => "blorgh" @@ -54,7 +54,7 @@ h4. Inside an engine h5. Critical files -At the root of this brand new engine's directory, lives a +blorgh.gemspec+ file. When you include the engine into the application later on, you will do so with this line in a 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+: gem 'blorgh', :path => "vendor/engines/blorgh" @@ -69,7 +69,7 @@ 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're wanting 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, 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. Within +lib/blorgh/engine.rb+ is the base class for the engine: @@ -83,25 +83,25 @@ 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 application's controllers. +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+ wouldn't be called +Post+, but instead be namespaced and called +Blorgh::Post+. In addition to this, the table for the model is namespaced, becoming +blorgh_posts+, rather than simply +posts+. Similar to the model namespacing, a controller called +PostsController+ would be +Blorgh::Postscontroller+ and the views for that controller would not be at +app/views/posts+, but rather +app/views/blorgh/posts+. Mailers would be namespaced as well. +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. h5. +app+ directory -Inside the +app+ directory there is 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. +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 is the +images+, +javascripts+ and +stylesheets+ directories which, again, you should be familiar with due to their similarities of 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/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 is called as such inside an engine -- rather than +EngineController+ -- mainly due to that if you consider that an engine is really just a mini-application, it makes sense. You should also be able to convert an application to an engine with relatively little pain, and this is just one of the ways to make that process easier, albeit however so slightly. +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 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 applications +app/views/layouts/application.html.erb+ file. +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. @@ -113,7 +113,7 @@ This directory contains one file, +script/rails+, which enables you to use the + 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. +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. h5. +test+ directory @@ -125,13 +125,13 @@ Rails.application.routes.draw do end -This line mounts the engine at the path of +/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. Also in the test directory is the +test/integration+ directory, where integration tests for the engine should be placed. Other directories can be created in the +test+ directory also. For example, you may wish to create a +test/unit+ directory for your unit tests. h3. Providing engine functionality -The engine that this guide covers will provide 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. h4. Generating a post resource @@ -189,7 +189,7 @@ 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. This is also what causes the engine's routes to be isolated from those routes that are within the application. This is discussed further in the "Routes":#routes section of this guide. -Next, the +scaffold_controller+ generator is invoked, generating a controlled called +Blorgh::PostsController+ (at +app/controllers/blorgh/posts_controller.rb+) and its related views at +app/views/blorgh/posts+. This generator also generates a functional test for the controller (+test/functional/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 functional test for the controller (+test/functional/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: @@ -361,7 +361,7 @@ Missing partial blorgh/comments/comment with {:handlers=>[:erb, :builder], :form * "/Users/ryan/Sites/side_projects/blorgh/app/views" -The engine is unable to find the partial required for rendering the comments. Rails has looked firstly 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: @@ -375,7 +375,7 @@ That completes the comment function of the blogging engine. Now it's time to use h3. 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 for it, 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. h4. Mounting the engine @@ -391,18 +391,12 @@ Usually, specifying the engine inside the Gemfile would be done by specifying it gem 'devise' -Because the +blorgh+ engine is still under development, it will need to have a +:path+ option for its +Gemfile+ specification: +However, because you are developing the +blorgh+ engine on your local machine, you will need to specify the +:path+ option in your +Gemfile+: gem 'blorgh', :path => "/path/to/blorgh" -If the whole +blorgh+ engine directory is copied to +vendor/engines/blorgh+ then it could be specified in the +Gemfile+ like this: - - -gem 'blorgh', :path => "vendor/engines/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. To make the engine's functionality accessible from within an application, it needs to be mounted in that application's +config/routes.rb+ file: @@ -458,7 +452,7 @@ h5. 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. -Usually, an application would have a +User+ class that would provide the objects that would represent the posts' and comments' authors, but there could be a case where the application calls this class something different, such as +Person+. It's because of this reason that the engine should not hardcode the associations to be exactly for a +User+ class, but should allow for some flexibility around what the class is called. +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: @@ -536,7 +530,7 @@ Finally, the author's name should be displayed on the post's page. Add this code

-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: # @@ -567,7 +561,7 @@ This change does require that the engine is run from a Rails application that ha h4. Configuring an engine -This section covers firstly how you can make the +user_class+ setting of the Blorgh engine 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. h5. Setting configuration settings in the application @@ -601,7 +595,7 @@ def self.user_class end -This would then turn the above code for +self.author=+ into this: +This would then turn the above code for +set_author+ into this: self.author = Blorgh.user_class.find_or_create_by_name(author_name) @@ -621,7 +615,7 @@ WARNING: It's very important here to use the +String+ version of the class, rath 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 class's API must be. The engine simply requires this class to define a +find_or_create_by_name+ 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_name+ 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. h5. General engine configuration @@ -661,11 +655,11 @@ h4. 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 applicaiton. 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+, and for complex class modifications, consider using +ActiveSupport::Concern+. h5. Implementing Decorator Pattern Using Class#class_eval -**Adding** Post#time_since_created, +**Adding** +Post#time_since_created+, # MyApp/app/decorators/models/blorgh/post_decorator.rb @@ -681,12 +675,12 @@ end # Blorgh/app/models/post.rb class Post < ActiveRecord::Base - :has_many :comments + has_many :comments end -**Overriding** Post#summary +**Overriding** +Post#summary+ # MyApp/app/decorators/models/blorgh/post_decorator.rb @@ -702,7 +696,7 @@ end # Blorgh/app/models/post.rb class Post < ActiveRecord::Base - :has_many :comments + has_many :comments def summary "#{title}" end @@ -712,10 +706,10 @@ end h5. 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. ["**ActiveSupport::Concern**":http://edgeapi.rubyonrails.org/classes/ActiveSupport/Concern.html] helps manage load order of interlinked dependencies 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+. ["**ActiveSupport::Concern**":http://edgeapi.rubyonrails.org/classes/ActiveSupport/Concern.html] helps manage the load order of interlinked dependencies at run time allowing you to significantly modularize your code. -**Adding** Post#time_since_created
-**Overriding** Post#summary +**Adding** +Post#time_since_created+
+**Overriding** +Post#summary+ # MyApp/app/models/blorgh/post.rb @@ -781,7 +775,7 @@ When Rails looks for a view to render, it will first look in the +app/views+ dir In the +blorgh+ engine, there is a currently a file at +app/views/blorgh/posts/index.html.erb+. When the engine is asked to render the view for +Blorgh::PostsController+'s +index+ action, it will first see if it can find it at +app/views/blorgh/posts/index.html.erb+ within the application and then if it cannot it will look inside the engine. -By overriding this view in the application, by simply creating a new file at +app/views/blorgh/posts/index.html.erb+, 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: @@ -856,10 +850,10 @@ INFO. Remember that in order to use languages like Sass or CoffeeScript, you sho h4. Separate Assets & Precompiling -There are some situations where your engine's assets not required by the host application. For example, say that you've created +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 +"blorg/admin.css"+ in it's stylesheets. In this situation, you should explicitly define these assets for precompilation. -This tells sprockets to add you engine assets when +rake assets:precompile+ is ran. +This tells sprockets to add your engine assets when +rake assets:precompile+ is ran. You can define assets for precompilation in +engine.rb+ @@ -873,8 +867,7 @@ For more information, read the "Asset Pipeline guide":http://guides.rubyonrails. h4. Other gem dependencies -Gem dependencies inside an engine should be specified inside the +.gemspec+ file -that's at the root of the engine. The reason for this is because the engine may +Gem dependencies inside an engine should be specified inside the +.gemspec+ file at the root of the engine. The reason for this is because the engine may be installed as a gem. If dependencies were to be specified inside the +Gemfile+, these would not be recognised by a traditional gem install and so they would not be installed, causing the engine to malfunction. @@ -899,7 +892,7 @@ the application. The development dependencies for the gem will only be used when the tests for the engine are running. Note that if you want to immediately require dependencies when the engine is -required, you should require them before engine's initialization. For example: +required, you should require them before the engine's initialization. For example: require 'other_engine/engine' diff --git a/guides/source/form_helpers.textile b/guides/source/form_helpers.textile index d507a04c1b..d704b78f83 100644 --- a/guides/source/form_helpers.textile +++ b/guides/source/form_helpers.textile @@ -82,7 +82,7 @@ h4. Multiple Hashes in Form Helper Calls The +form_tag+ helper accepts 2 arguments: the path for the action and an options hash. This hash specifies the method of form submission and HTML options such as the form element's class. -As with the +link_to+ helper, the path argument doesn't have to be given a string; it can be a hash of URL parameters recognizable by Rails' routing mechanism, which will turn the hash into a valid URL. However, since both arguments to +form_tag+ are hashes, you can easily run into a problem if you would like to specify both. For instance, let's say you write this: +As with the +link_to+ helper, the path argument doesn't have to be a string; it can be a hash of URL parameters recognizable by Rails' routing mechanism, which will turn the hash into a valid URL. However, since both arguments to +form_tag+ are hashes, you can easily run into a problem if you would like to specify both. For instance, let's say you write this: form_tag(:controller => "people", :action => "search", :method => "get", :class => "nifty_form") @@ -942,4 +942,4 @@ As a convenience you can instead pass the symbol +:all_blank+ which will create h4. Adding Fields on the Fly -Rather than rendering multiple sets of fields ahead of time you may wish to add them only when a user clicks on an 'Add new child' button. Rails does not provide any builtin support for this. When generating new sets of fields you must ensure the the key of the associated array is unique - the current javascript date (milliseconds after the epoch) is a common choice. \ No newline at end of file +Rather than rendering multiple sets of fields ahead of time you may wish to add them only when a user clicks on an 'Add new child' button. Rails does not provide any builtin support for this. When generating new sets of fields you must ensure the the key of the associated array is unique - the current javascript date (milliseconds after the epoch) is a common choice. diff --git a/guides/source/i18n.textile b/guides/source/i18n.textile index 67863e590c..c073a146a8 100644 --- a/guides/source/i18n.textile +++ b/guides/source/i18n.textile @@ -575,11 +575,14 @@ The +:count+ interpolation variable has a special role in that it both is interp I18n.backend.store_translations :en, :inbox => { - :one => '1 message', + :one => 'one message', :other => '%{count} messages' } I18n.translate :inbox, :count => 2 # => '2 messages' + +I18n.translate :inbox, :count => 1 +# => 'one message' The algorithm for pluralizations in +:en+ is as simple as: diff --git a/guides/source/layouts_and_rendering.textile b/guides/source/layouts_and_rendering.textile index 32ceecea18..9eb8161c54 100644 --- a/guides/source/layouts_and_rendering.textile +++ b/guides/source/layouts_and_rendering.textile @@ -1185,7 +1185,7 @@ h5. Spacer Templates Rails will render the +_product_ruler+ partial (with no data passed in to it) between each pair of +_product+ partials. -h5. Partial Layouts +h5(#collection-partial-layouts). Partial Layouts When rendering collections it is also possible to use the +:layout+ option: diff --git a/guides/source/migrations.textile b/guides/source/migrations.textile index 587be269f7..e89bde7a83 100644 --- a/guides/source/migrations.textile +++ b/guides/source/migrations.textile @@ -81,7 +81,7 @@ it to default to +false+ for new users, but existing users are considered to have already opted in, so we use the User model to set the flag to +true+ for existing users. -h4. Using the change method +h4. Using the +change+ Method Rails 3.1 makes migrations smarter by providing a new change method. This method is preferred for writing constructive migrations (adding columns or @@ -351,7 +351,7 @@ end This migration will create a user_id column and appropriate index. -h4. Supported type modifiers +h4. Supported Type Modifiers You can also specify some options just after the field type between curly braces. You can use the following modifiers: @@ -550,7 +550,7 @@ and "ActiveRecord::ConnectionAdapters::Table":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html (which provides the methods available on the object yielded by +change_table+). -h4. Using the +change+ Method +h4. When to Use the +change+ Method The +change+ method removes the need to write both +up+ and +down+ methods in those cases that Rails knows how to revert the changes automatically. Currently, @@ -672,7 +672,7 @@ Neither of these Rake tasks do anything you could not do with +db:migrate+. They are simply more convenient, since you do not need to explicitly specify the version to migrate to. -h4. Resetting the database +h4. Resetting the Database The +rake db:reset+ task will drop the database, recreate it and load the current schema into it. @@ -680,7 +680,7 @@ current schema into it. NOTE: This is not the same as running all the migrations - see the section on "schema.rb":#schema-dumping-and-you. -h4. Running specific migrations +h4. Running Specific Migrations If you need to run a specific migration up or down, the +db:migrate:up+ and +db:migrate:down+ tasks will do that. Just specify the appropriate version and @@ -695,7 +695,7 @@ will run the +up+ method from the 20080906120000 migration. This task will first check whether the migration is already performed and will do nothing if Active Record believes that it has already been run. -h4. Changing the output of running migrations +h4. Changing the Output of Running Migrations By default migrations tell you exactly what they're doing and how long it took. A migration creating a table and adding an index might produce output like this diff --git a/guides/source/routing.textile b/guides/source/routing.textile index bed7d03e06..9718887612 100644 --- a/guides/source/routing.textile +++ b/guides/source/routing.textile @@ -644,10 +644,10 @@ You can also reuse dynamic segments from the match in the path to redirect to: get "/stories/:name" => redirect("/posts/%{name}") -You can also provide a block to redirect, which receives the params and (optionally) the request object: +You can also provide a block to redirect, which receives the params and the request object: -get "/stories/:name" => redirect {|params| "/posts/#{params[:name].pluralize}" } +get "/stories/:name" => redirect {|params, req| "/posts/#{params[:name].pluralize}" } get "/stories" => redirect {|p, req| "/posts/#{req.subdomain}" } diff --git a/guides/source/upgrading_ruby_on_rails.textile b/guides/source/upgrading_ruby_on_rails.textile index 70d4dabfe2..b750cc2ffb 100644 --- a/guides/source/upgrading_ruby_on_rails.textile +++ b/guides/source/upgrading_ruby_on_rails.textile @@ -44,7 +44,7 @@ The delete method in collection associations can now receive Fixnum Rails 4.0 has changed how orders get stacked in +ActiveRecord::Relation+. In previous versions of rails new order was applied after previous defined order. But this is no long true. Check "ActiveRecord Query guide":active_record_querying.html#ordering for more information. -Rails 4.0 has changed serialized_attributes and _attr_readonly to class methods only. Now you shouldn't use instance methods, it's deprecated. You must change them, e.g. self.serialized_attributes to self.class.serialized_attributes. +Rails 4.0 has changed serialized_attributes and _attr_readonly to class methods only. Now you shouldn't use instance methods, it's deprecated. You must change them, e.g. self.serialized_attributes to self.class.serialized_attributes. h4(#active_model4_0). Active Model