diff options
Diffstat (limited to 'guides')
41 files changed, 1490 insertions, 814 deletions
diff --git a/guides/code/getting_started/app/views/comments/_comment.html.erb b/guides/code/getting_started/app/views/comments/_comment.html.erb index 0cebe0bd96..3d2bc1590e 100644 --- a/guides/code/getting_started/app/views/comments/_comment.html.erb +++ b/guides/code/getting_started/app/views/comments/_comment.html.erb @@ -10,6 +10,6 @@ <p> <%= link_to 'Destroy Comment', [comment.post, comment], - :confirm => 'Are you sure?', - :method => :delete %> + :method => :delete, + :data => { :confirm => 'Are you sure?' } %> </p> diff --git a/guides/code/getting_started/app/views/posts/index.html.erb b/guides/code/getting_started/app/views/posts/index.html.erb index 7b72720d50..9a0e90eadc 100644 --- a/guides/code/getting_started/app/views/posts/index.html.erb +++ b/guides/code/getting_started/app/views/posts/index.html.erb @@ -17,7 +17,7 @@ <td><%= post.text %></td> <td><%= link_to 'Show', :action => :show, :id => post.id %> <td><%= link_to 'Edit', :action => :edit, :id => post.id %> - <td><%= link_to 'Destroy', { :action => :destroy, :id => post.id }, :method => :delete, :confirm => 'Are you sure?' %> + <td><%= link_to 'Destroy', { :action => :destroy, :id => post.id }, :method => :delete, :data => { :confirm => 'Are you sure?' } %> </tr> <% end %> </table> diff --git a/guides/code/getting_started/config/initializers/session_store.rb b/guides/code/getting_started/config/initializers/session_store.rb index 1a67af58b5..3b2ca93ab9 100644 --- a/guides/code/getting_started/config/initializers/session_store.rb +++ b/guides/code/getting_started/config/initializers/session_store.rb @@ -1,8 +1,3 @@ # Be sure to restart your server when you modify this file. Blog::Application.config.session_store :cookie_store, key: '_blog_session' - -# Use the database for sessions instead of the cookie-based default, -# which shouldn't be used to store highly confidential information -# (create the session table with "rails generate session_migration") -# Blog::Application.config.session_store :active_record_store diff --git a/guides/rails_guides/textile_extensions.rb b/guides/rails_guides/textile_extensions.rb index 0a002a785f..1faddd4ca0 100644 --- a/guides/rails_guides/textile_extensions.rb +++ b/guides/rails_guides/textile_extensions.rb @@ -1,5 +1,3 @@ -require 'active_support/core_ext/object/inclusion' - module RedCloth::Formatters::HTML def emdash(opts) "--" diff --git a/guides/source/2_2_release_notes.textile b/guides/source/2_2_release_notes.textile index 3a0f2efbaf..eb4b32329b 100644 --- a/guides/source/2_2_release_notes.textile +++ b/guides/source/2_2_release_notes.textile @@ -118,9 +118,9 @@ h4. Transactional Migrations Historically, multiple-step Rails migrations have been a source of trouble. If something went wrong during a migration, everything before the error changed the database and everything after the error wasn't applied. Also, the migration version was stored as having been executed, which means that it couldn't be simply rerun by +rake db:migrate:redo+ after you fix the problem. Transactional migrations change this by wrapping migration steps in a DDL transaction, so that if any of them fail, the entire migration is undone. In Rails 2.2, transactional migrations are supported on PostgreSQL out of the box. The code is extensible to other database types in the future - and IBM has already extended it to support the DB2 adapter. -* Lead Contributor: "Adam Wiggins":http://adam.blog.heroku.com/ +* Lead Contributor: "Adam Wiggins":http://adam.heroku.com/ * More information: -** "DDL Transactions":http://adam.blog.heroku.com/past/2008/9/3/ddl_transactions/ +** "DDL Transactions":http://adam.heroku.com/past/2008/9/3/ddl_transactions/ ** "A major milestone for DB2 on Rails":http://db2onrails.com/2008/11/08/a-major-milestone-for-db2-on-rails/ h4. Connection Pooling diff --git a/guides/source/2_3_release_notes.textile b/guides/source/2_3_release_notes.textile index 15abba66ab..36f425574b 100644 --- a/guides/source/2_3_release_notes.textile +++ b/guides/source/2_3_release_notes.textile @@ -561,7 +561,7 @@ This will layer the changes from the template on top of whatever code the projec h4. Quieter Backtraces -Building on Thoughtbot's "Quiet Backtrace":http://www.thoughtbot.com/projects/quietbacktrace plugin, which allows you to selectively remove lines from +Test::Unit+ backtraces, Rails 2.3 implements +ActiveSupport::BacktraceCleaner+ and +Rails::BacktraceCleaner+ in core. This supports both filters (to perform regex-based substitutions on backtrace lines) and silencers (to remove backtrace lines entirely). Rails automatically adds silencers to get rid of the most common noise in a new application, and builds a +config/backtrace_silencers.rb+ file to hold your own additions. This feature also enables prettier printing from any gem in the backtrace. +Building on Thoughtbot's "Quiet Backtrace":https://github.com/thoughtbot/quietbacktrace plugin, which allows you to selectively remove lines from +Test::Unit+ backtraces, Rails 2.3 implements +ActiveSupport::BacktraceCleaner+ and +Rails::BacktraceCleaner+ in core. This supports both filters (to perform regex-based substitutions on backtrace lines) and silencers (to remove backtrace lines entirely). Rails automatically adds silencers to get rid of the most common noise in a new application, and builds a +config/backtrace_silencers.rb+ file to hold your own additions. This feature also enables prettier printing from any gem in the backtrace. h4. Faster Boot Time in Development Mode with Lazy Loading/Autoload diff --git a/guides/source/4_0_release_notes.textile b/guides/source/4_0_release_notes.textile index 3733fec950..fc922b04b7 100644 --- a/guides/source/4_0_release_notes.textile +++ b/guides/source/4_0_release_notes.textile @@ -82,8 +82,6 @@ will generate the model with <tt>belongs_to :supplier, polymorphic: true</tt> as * Load all environments available in <tt>config.paths["config/environments"]</tt>. -* The application generator generates <tt>public/humans.txt</tt> with some basic data. - * Add <tt>config.queue_consumer</tt> to allow the default consumer to be configurable. * Add <tt>Rails.queue</tt> as an interface with a default implementation that consumes jobs in a separate thread. @@ -118,10 +116,34 @@ 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 <tt>:delivery_method_options</tt> key on mail. + +<ruby> +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 +</ruby> + h3. Action Pack h4. Action Controller +* Add <tt>ActionController::Flash.add_flash_types</tt> method to allow people to register their own flash types. e.g.: + +<ruby> +class ApplicationController + add_flash_types :error, :warning +end +</ruby> + +If you add the above code, you can use <tt><%= error %></tt> in an erb, and <tt>redirect_to /foo, :error => 'message'</tt> in a controller. + * Remove Active Model dependency from Action Pack. * Support unicode characters in routes. Route will be automatically escaped, so instead of manually escaping: @@ -186,6 +208,39 @@ h5(#actioncontroller_deprecations). Deprecations h4. Action Dispatch +* Add Routing Concerns to declare common routes that can be reused inside others resources and routes. + +Code before: + +<ruby> +resources :messages do + resources :comments +end + +resources :posts do + resources :comments + resources :images, only: :index +end +</ruby> + +Code after: + +<ruby> +concern :commentable do + resources :comments +end + +concern :image_attachable do + resources :images, only: :index +end + +resources :messages, concerns: :commentable + +resources :posts, concerns: [:commentable, :image_attachable] +</ruby> + +* Show routes in exception page while debugging a <tt>RoutingError</tt> in development. + * Include <tt>mounted_helpers</tt> (helpers for accessing mounted engines) in <tt>ActionDispatch::IntegrationTest</tt> by default. * Added <tt>ActionDispatch::SSL</tt> middleware that when included force all the requests to be under HTTPS protocol. @@ -362,6 +417,8 @@ User.where(:age => 30).to_a.inspect # => [#<User ...>, #<User ...>] </ruby> +if more than 10 items are returned by the relation, inspect will only show the first 10 followed by ellipsis. + * Add <tt>:collation</tt> and <tt>:ctype</tt> support to PostgreSQL. These are available for PostgreSQL 8.4 or later. <yaml> @@ -436,35 +493,10 @@ end User.stored_attributes[:settings] # [:color, :homepage] </ruby> -* <tt>composed_of</tt> was removed. You'll have to write your own accessor and mutator methods if you'd like to use value objects to represent some portion of your models. So, instead of: - -<ruby> -class Person < ActiveRecord::Base - composed_of :address, :mapping => [ %w(address_street street), %w(address_city city) ] -end -</ruby> - -you could write something like this: - -<ruby> -def address - @address ||= Address.new(address_street, address_city) -end - -def address=(address) - self[:address_street] = @address.street - self[:address_city] = @address.city - - @address = address -end -</ruby> - * PostgreSQL default log level is now 'warning', to bypass the noisy notice messages. You can change the log level using the <tt>min_messages</tt> option available in your <tt>config/database.yml</tt>. * Add uuid datatype support to PostgreSQL adapter. -* <tt>update_attribute</tt> has been removed. Use <tt>update_column</tt> if you want to bypass mass-assignment protection, validations, callbacks, and touching of updated_at. Otherwise please use <tt>update_attributes</tt>. - * Added <tt>ActiveRecord::Migration.check_pending!</tt> that raises an error if migrations are pending. * Added <tt>#destroy!</tt> which acts like <tt>#destroy</tt> but will raise an <tt>ActiveRecord::RecordNotDestroyed</tt> exception instead of returning <tt>false</tt>. @@ -685,6 +717,10 @@ where(...).remove_conditions # => still has conditions * New rails application would be generated with the config.active_record.dependent_restrict_raises = false in the application config. +* The migration generator now creates a join table with (commented) indexes every time the migration name contains the word "join_table". + +* <tt>ActiveRecord::SessionStore</tt> is removed from Rails 4.0 and is now a separate "gem":https://github.com/rails/activerecord-session_store. + h3. Active Model * Changed <tt>AM::Serializers::JSON.include_root_in_json</tt> default value to false. Now, AM Serializers and AR objects have the same default behaviour. @@ -731,6 +767,8 @@ h3. Active Resource h3. Active Support +* Add default values to all <tt>ActiveSupport::NumberHelper</tt> methods, to avoid errors with empty locales or missing values. + * <tt>Time#change</tt> now works with time values with offsets other than UTC or the local time zone. * Add <tt>Time#prev_quarter</tt> and <tt>Time#next_quarter</tt> short-hands for <tt>months_ago(3)</tt> and <tt>months_since(3)</tt>. diff --git a/guides/source/action_controller_overview.textile b/guides/source/action_controller_overview.textile index cc3350819b..1d43b44391 100644 --- a/guides/source/action_controller_overview.textile +++ b/guides/source/action_controller_overview.textile @@ -168,8 +168,8 @@ h3. Session Your application has a session for each user in which you can store small amounts of data that will be persisted between requests. The session is only available in the controller and the view and can use one of a number of different storage mechanisms: * ActionDispatch::Session::CookieStore - Stores everything on the client. -* ActiveRecord::SessionStore - Stores the data in a database using Active Record. * ActionDispatch::Session::CacheStore - Stores the data in the Rails cache. +* ActionDispatch::Session::ActiveRecordStore - Stores the data in a database using Active Record. (require `activerecord-session_store` gem). * ActionDispatch::Session::MemCacheStore - Stores the data in a memcached cluster (this is a legacy implementation; consider using CacheStore instead). All session stores use a cookie to store a unique ID for each session (you must use a cookie, Rails will not allow you to pass the session ID in the URL as this is less secure). @@ -187,7 +187,7 @@ If you need a different session storage mechanism, you can change it in the +con <ruby> # Use the database for sessions instead of the cookie-based default, # which shouldn't be used to store highly confidential information -# (create the session table with "script/rails g session_migration") +# (create the session table with "script/rails g active_record:session_migration") # YourApp::Application.config.session_store :active_record_store </ruby> @@ -278,26 +278,31 @@ To reset the entire session, use +reset_session+. h4. The Flash -The flash is a special part of the session which is cleared with each request. This means that values stored there will only be available in the next request, which is useful for storing error messages etc. It is accessed in much the same way as the session, like a hash. Let's use the act of logging out as an example. The controller can send a message which will be displayed to the user on the next request: +The flash is a special part of the session which is cleared with each request. This means that values stored there will only be available in the next request, which is useful for passing error messages etc. + +It is accessed in much the same way as the session, as a hash (it's a "FlashHash":http://api.rubyonrails.org/classes/ActionDispatch/Flash/FlashHash.html instance). + +Let's use the act of logging out as an example. The controller can send a message which will be displayed to the user on the next request: <ruby> class LoginsController < ApplicationController def destroy session[:current_user_id] = nil - flash[:notice] = "You have successfully logged out" + flash[:notice] = "You have successfully logged out." redirect_to root_url end end </ruby> -Note it is also possible to assign a flash message as part of the redirection. +Note that it is also possible to assign a flash message as part of the redirection. You can assign +:notice+, +:alert+ or the general purpose +:flash+: <ruby> -redirect_to root_url, :notice => "You have successfully logged out" +redirect_to root_url, :notice => "You have successfully logged out." +redirect_to root_url, :alert => "You're stuck here!" +redirect_to root_url, :flash => { :referral_code => 1234 } </ruby> - -The +destroy+ action redirects to the application's +root_url+, where the message will be displayed. Note that it's entirely up to the next action to decide what, if anything, it will do with what the previous action put in the flash. It's conventional to display eventual errors or notices from the flash in the application's layout: +The +destroy+ action redirects to the application's +root_url+, where the message will be displayed. Note that it's entirely up to the next action to decide what, if anything, it will do with what the previous action put in the flash. It's conventional to display any error alerts or notices from the flash in the application's layout: <ruby> <html> @@ -306,15 +311,23 @@ The +destroy+ action redirects to the application's +root_url+, where the messag <% if flash[:notice] %> <p class="notice"><%= flash[:notice] %></p> <% end %> - <% if flash[:error] %> - <p class="error"><%= flash[:error] %></p> + <% if flash[:alert] %> + <p class="alert"><%= flash[:alert] %></p> <% end %> <!-- more content --> </body> </html> </ruby> -This way, if an action sets an error or a notice message, the layout will display it automatically. +This way, if an action sets a notice or an alert message, the layout will display it automatically. + +You can pass anything that the session can store; you're not limited to notices and alerts: + +<ruby> +<% if flash[:just_signed_up] %> + <p class="welcome">Welcome to our site!</p> +<% end %> +</ruby> If you want a flash value to be carried over to another request, use the +keep+ method: @@ -478,9 +491,9 @@ class ChangesController < ActionController::Base end </ruby> -Note that an around filter wraps also rendering. In particular, if in the example above the view itself reads from the database via a scope or whatever, it will do so within the transaction and thus present the data to preview. +Note that an around filter also wraps rendering. In particular, if in the example above, the view itself reads from the database (e.g. via a scope), it will do so within the transaction and thus present the data to preview. -They can choose not to yield and build the response themselves, in which case the action is not run. +You can choose not to yield and build the response yourself, in which case the action will not be run. h4. Other Ways to Use Filters diff --git a/guides/source/action_mailer_basics.textile b/guides/source/action_mailer_basics.textile index 7c61cc4a8d..6d04a76088 100644 --- a/guides/source/action_mailer_basics.textile +++ b/guides/source/action_mailer_basics.textile @@ -4,7 +4,7 @@ This guide should provide you with all you need to get started in sending and re endprologue. -WARNING. This Guide is based on Rails 3.2. Some of the code shown here will not work in earlier versions of Rails. +WARNING. This guide is based on Rails 3.2. Some of the code shown here will not work in earlier versions of Rails. h3. Introduction @@ -84,7 +84,7 @@ Create a file called +welcome_email.html.erb+ in +app/views/user_mailer/+. This </html> </erb> -It is also a good idea to make a text part for this email, to do this, create a file called +welcome_email.text.erb+ in +app/views/user_mailer/+: +It is also a good idea to make a text part for this email. To do this, create a file called +welcome_email.text.erb+ in +app/views/user_mailer/+: <erb> Welcome to example.com, <%= @user.name %> @@ -144,7 +144,7 @@ The method +welcome_email+ returns a <tt>Mail::Message</tt> object which can the NOTE: In previous versions of Rails, you would call +deliver_welcome_email+ or +create_welcome_email+. This has been deprecated in Rails 3.0 in favour of just calling the method name itself. -WARNING: Sending out an email should only take a fraction of a second, but if you are planning on sending out many emails, or you have a slow domain resolution service, you might want to investigate using a background process like Delayed Job. +WARNING: Sending out an email should only take a fraction of a second. If you are planning on sending out many emails, or you have a slow domain resolution service, you might want to investigate using a background process like Delayed Job. h4. Auto encoding header values @@ -152,14 +152,14 @@ Action Mailer now handles the auto encoding of multibyte characters inside of he If you are using UTF-8 as your character set, you do not have to do anything special, just go ahead and send in UTF-8 data to the address fields, subject, keywords, filenames or body of the email and Action Mailer will auto encode it into quoted printable for you in the case of a header field or Base64 encode any body parts that are non US-ASCII. -For more complex examples such as defining alternate character sets or self encoding text first, please refer to the Mail library. +For more complex examples such as defining alternate character sets or self-encoding text first, please refer to the Mail library. h4. Complete List of Action Mailer Methods There are just three methods that you need to send pretty much any email message: -* <tt>headers</tt> - Specifies any header on the email you want, you can pass a hash of header field names and value pairs, or you can call <tt>headers[:field_name] = 'value'</tt> -* <tt>attachments</tt> - Allows you to add attachments to your email, for example <tt>attachments['file-name.jpg'] = File.read('file-name.jpg')</tt> +* <tt>headers</tt> - Specifies any header on the email you want. You can pass a hash of header field names and value pairs, or you can call <tt>headers[:field_name] = 'value'</tt>. +* <tt>attachments</tt> - Allows you to add attachments to your email. For example, <tt>attachments['file-name.jpg'] = File.read('file-name.jpg')</tt>. * <tt>mail</tt> - Sends the actual email itself. You can pass in headers as a hash to the mail method as a parameter, mail will then create an email, either plain text, or multipart, depending on what email templates you have defined. h5. Custom Headers @@ -184,7 +184,7 @@ headers["X-Spam"] = value headers {"X-Spam" => value, "X-Special" => another_value} </ruby> -TIP: All <tt>X-Value</tt> headers per the RFC2822 can appear more than one time. If you want to delete an <tt>X-Value</tt> header, you need to assign it a value of <tt>nil</tt>. +TIP: All <tt>X-Value</tt> headers per the RFC2822 can appear more than once. If you want to delete an <tt>X-Value</tt> header, you need to assign it a value of <tt>nil</tt>. h5. Adding Attachments @@ -196,7 +196,7 @@ Adding attachments has been simplified in Action Mailer 3.0. attachments['filename.jpg'] = File.read('/path/to/filename.jpg') </ruby> -NOTE: Mail will automatically Base64 encode an attachment, if you want something different, pre-encode your content and pass in the encoded content and encoding in a +Hash+ to the +attachments+ method. +NOTE: Mail will automatically Base64 encode an attachment. If you want something different, pre-encode your content and pass in the encoded content and encoding in a +Hash+ to the +attachments+ method. * Pass the file name and specify headers and content and Action Mailer and Mail will use the settings you pass in. @@ -229,7 +229,7 @@ end <%= image_tag attachments['image.jpg'].url %> </erb> -* As this is a standard call to +image_tag+ you can pass in an options hash after the attachment url as you could for any other image: +* As this is a standard call to +image_tag+ you can pass in an options hash after the attachment URL as you could for any other image: <erb> <p>Hello there, this is our image</p> @@ -240,7 +240,7 @@ end h5. Sending Email To Multiple Recipients -It is possible to send email to one or more recipients in one email (for e.g. informing all admins of a new signup) by setting the list of emails to the <tt>:to</tt> key. The list of emails can be an array of email addresses or a single string with the addresses separated by commas. +It is possible to send email to one or more recipients in one email (e.g., informing all admins of a new signup) by setting the list of emails to the <tt>:to</tt> key. The list of emails can be an array of email addresses or a single string with the addresses separated by commas. <ruby> class AdminMailer < ActionMailer::Base @@ -410,6 +410,21 @@ end The above will send a multipart email with an attachment, properly nested with the top level being <tt>multipart/mixed</tt> and the first part being a <tt>multipart/alternative</tt> 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. + +<ruby> +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 +</ruby> + 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: @@ -451,13 +466,13 @@ The following configuration options are best made in one of the environment file |+template_root+|Determines the base from which template references will be made.| |+logger+|Generates information on the mailing run if available. Can be set to +nil+ for no logging. Compatible with both Ruby's own +Logger+ and +Log4r+ loggers.| -|+smtp_settings+|Allows detailed configuration for <tt>:smtp</tt> delivery method:<ul><li><tt>:address</tt> - Allows you to use a remote mail server. Just change it from its default "localhost" setting.</li><li><tt>:port</tt> - On the off chance that your mail server doesn't run on port 25, you can change it.</li><li><tt>:domain</tt> - If you need to specify a HELO domain, you can do it here.</li><li><tt>:user_name</tt> - If your mail server requires authentication, set the username in this setting.</li><li><tt>:password</tt> - If your mail server requires authentication, set the password in this setting.</li><li><tt>:authentication</tt> - If your mail server requires authentication, you need to specify the authentication type here. This is a symbol and one of <tt>:plain</tt>, <tt>:login</tt>, <tt>:cram_md5</tt>.</li></ul>| +|+smtp_settings+|Allows detailed configuration for <tt>:smtp</tt> delivery method:<ul><li><tt>:address</tt> - Allows you to use a remote mail server. Just change it from its default "localhost" setting.</li><li><tt>:port</tt> - On the off chance that your mail server doesn't run on port 25, you can change it.</li><li><tt>:domain</tt> - If you need to specify a HELO domain, you can do it here.</li><li><tt>:user_name</tt> - If your mail server requires authentication, set the username in this setting.</li><li><tt>:password</tt> - If your mail server requires authentication, set the password in this setting.</li><li><tt>:authentication</tt> - If your mail server requires authentication, you need to specify the authentication type here. This is a symbol and one of <tt>:plain</tt>, <tt>:login</tt>, <tt>:cram_md5</tt>.</li><li><tt>:enable_starttls_auto</tt> - Set this to <tt>false</tt> if there is a problem with your server certificate that you cannot resolve.</li></ul>| |+sendmail_settings+|Allows you to override options for the <tt>:sendmail</tt> delivery method.<ul><li><tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.</li><li><tt>:arguments</tt> - The command line arguments to be passed to sendmail. Defaults to <tt>-i -t</tt>.</li></ul>| |+raise_delivery_errors+|Whether or not errors should be raised if the email fails to be delivered.| |+delivery_method+|Defines a delivery method. Possible values are <tt>:smtp</tt> (default), <tt>:sendmail</tt>, <tt>:file</tt> and <tt>:test</tt>.| |+perform_deliveries+|Determines whether deliveries are actually carried out when the +deliver+ method is invoked on the Mail message. By default they are, but this can be turned off to help functional testing.| |+deliveries+|Keeps an array of all the emails sent out through the Action Mailer with delivery_method :test. Most useful for unit and functional testing.| -|+async+|Setting this flag will turn on asynchronous message sending, message rendering and delivery will be pushed to <tt>Rails.queue</tt> for processing.| +|+default_options+|Allows you to set default values for the <tt>mail</tt> method options (<tt>:from</tt>, <tt>:reply_to</tt>, etc.).| h4. Example Action Mailer Configuration @@ -472,6 +487,7 @@ config.action_mailer.delivery_method = :sendmail # } config.action_mailer.perform_deliveries = true config.action_mailer.raise_delivery_errors = true +config.action_mailer.default_options = {from: "no-replay@example.org"} </ruby> h4. Action Mailer Configuration for GMail @@ -508,8 +524,8 @@ class UserMailerTest < ActionMailer::TestCase # Test the body of the sent email contains what we expect it to assert_equal [user.email], email.to assert_equal "Welcome to My Awesome Site", email.subject - assert_match(/<h1>Welcome to example.com, #{user.name}<\/h1>/, email.encoded) - assert_match(/Welcome to example.com, #{user.name}/, email.encoded) + assert_match "<h1>Welcome to example.com, #{user.name}</h1>", email.body.to_s + assert_match "you have joined to example.com community", email.body.to_s end end </ruby> @@ -518,19 +534,7 @@ In the test we send the email and store the returned object in the +email+ varia h3. Asynchronous -You can turn on application-wide asynchronous message sending by adding to your <tt>config/application.rb</tt> file: - -<ruby> -config.action_mailer.async = true -</ruby> - -Alternatively you can turn on async within specific mailers: - -<ruby> -class WelcomeMailer < ActionMailer::Base - self.async = true -end -</ruby> +Rails provides a Synchronous Queue by default. If you want to use an Asynchronous one you will need to configure an async Queue provider like Resque. Queue providers are supposed to have a Railtie where they configure it's own async queue. h4. Custom Queues diff --git a/guides/source/action_view_overview.textile b/guides/source/action_view_overview.textile index fdfa97effa..33ae7f6933 100644 --- a/guides/source/action_view_overview.textile +++ b/guides/source/action_view_overview.textile @@ -785,7 +785,7 @@ h5. content_for Calling +content_for+ stores a block of markup in an identifier for later use. You can make subsequent calls to the stored content in other templates or the layout by passing the identifier as an argument to +yield+. -For example, let's say we have a standard application layout, but also a special page that requires certain Javascript that the rest of the site doesn't need. We can use +content_for+ to include this Javascript on our special page without fattening up the rest of the site. +For example, let's say we have a standard application layout, but also a special page that requires certain JavaScript that the rest of the site doesn't need. We can use +content_for+ to include this JavaScript on our special page without fattening up the rest of the site. *app/views/layouts/application.html.erb* @@ -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) </ruby> -If <tt>@post.author_ids</tt> is [1], this would return: +If <tt>@post.author_ids</tt> is <tt><notextile>[1]</notextile></tt>, this would return: <html> <input id="post_author_ids_1" name="post[author_ids][]" type="checkbox" value="1" checked="checked" /> diff --git a/guides/source/active_model_basics.textile b/guides/source/active_model_basics.textile index 7cafff2ad8..2c30ddb84c 100644 --- a/guides/source/active_model_basics.textile +++ b/guides/source/active_model_basics.textile @@ -1,18 +1,18 @@ h2. Active Model Basics -This guide should provide you with all you need to get started using model classes. Active Model allow for Action Pack helpers to interact with non-ActiveRecord models. Active Model also helps building custom ORMs for use outside of the Rails framework. +This guide should provide you with all you need to get started using model classes. Active Model allows for Action Pack helpers to interact with non-ActiveRecord models. Active Model also helps building custom ORMs for use outside of the Rails framework. endprologue. -WARNING. This Guide is based on Rails 3.0. Some of the code shown here will not work in earlier versions of Rails. +WARNING. This guide is based on Rails 3.0. Some of the code shown here will not work in earlier versions of Rails. h3. Introduction -Active Model is a library containing various modules used in developing frameworks that need to interact with the Rails Action Pack library. Active Model provides a known set of interfaces for usage in classes. Some of modules are explained below - +Active Model is a library containing various modules used in developing frameworks that need to interact with the Rails Action Pack library. Active Model provides a known set of interfaces for usage in classes. Some of modules are explained below. h4. AttributeMethods -AttributeMethods module can add custom prefixes and suffixes on methods of a class. It is used by defining the prefixes and suffixes, which methods on the object will use them. +The AttributeMethods module can add custom prefixes and suffixes on methods of a class. It is used by defining the prefixes and suffixes, which methods on the object will use them. <ruby> class Person @@ -92,7 +92,7 @@ person.to_param #=> nil h4. Dirty -An object becomes dirty when an object is gone through one or more changes to its attributes and not yet saved. This gives the ability to check whether an object has been changed or not. It also has attribute based accessor methods. Lets consider a Person class with attributes first_name and last_name +An object becomes dirty when it has gone through one or more changes to its attributes and has not been saved. This gives the ability to check whether an object has been changed or not. It also has attribute based accessor methods. Let's consider a Person class with attributes first_name and last_name <ruby> require 'active_model' @@ -168,7 +168,7 @@ Track what was the previous value of the attribute. person.first_name_was #=> "First Name" </ruby> -Track both previous and current value of the changed attribute. Returns an array if changed else returns nil +Track both previous and current value of the changed attribute. Returns an array if changed, else returns nil. <ruby> #attr_name_change diff --git a/guides/source/active_record_querying.textile b/guides/source/active_record_querying.textile index 101988c59e..96ae5b2972 100644 --- a/guides/source/active_record_querying.textile +++ b/guides/source/active_record_querying.textile @@ -12,8 +12,6 @@ This guide covers different ways to retrieve data from the database using Active endprologue. -WARNING. This Guide is based on Rails 3.0. Some of the code shown here will not work in other versions of Rails. - If you're used to using raw SQL to find database records, then you will generally find that there are better ways to carry out the same operations in Rails. Active Record insulates you from the need to use SQL in most cases. Code examples throughout this guide will refer to one or more of the following models: @@ -53,20 +51,29 @@ h3. Retrieving Objects from the Database To retrieve objects from the database, Active Record provides several finder methods. Each finder method allows you to pass arguments into it to perform certain queries on your database without writing raw SQL. The methods are: -* +where+ -* +select+ +* +bind+ +* +create_with+ +* +eager_load+ +* +extending+ +* +from+ * +group+ -* +order+ -* +reorder+ -* +reverse_order+ -* +limit+ -* +offset+ -* +joins+ +* +having+ * +includes+ +* +joins+ +* +limit+ * +lock+ +* +none+ +* +offset+ +* +order+ +* +none+ +* +preload+ * +readonly+ -* +from+ -* +having+ +* +references+ +* +reorder+ +* +reverse_order+ +* +select+ +* +uniq+ +* +where+ All of the above methods return an instance of <tt>ActiveRecord::Relation</tt>. @@ -259,7 +266,7 @@ SELECT * FROM clients WHERE (clients.id IN (1,10)) WARNING: <tt>Model.find(array_of_primary_key)</tt> will raise an +ActiveRecord::RecordNotFound+ exception unless a matching record is found for <strong>all</strong> of the supplied primary keys. -h5. take +h5(#take-n-objects). take <tt>Model.take(limit)</tt> retrieves the first number of records specified by +limit+ without any explicit ordering: @@ -275,7 +282,7 @@ The SQL equivalent of the above is: SELECT * FROM clients LIMIT 2 </sql> -h5. first +h5(#first-n-objects). first <tt>Model.first(limit)</tt> finds the first number of records specified by +limit+ ordered by primary key: @@ -291,7 +298,7 @@ The SQL equivalent of the above is: SELECT * FROM clients LIMIT 2 </sql> -h5. last +h5(#last-n-objects). last <tt>Model.last(limit)</tt> finds the number of records specified by +limit+ ordered by primary key in descending order: @@ -488,7 +495,7 @@ This code will generate SQL like this: SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5)) </sql> -h3. Ordering +h3(#ordering). Ordering To retrieve records from the database in a specific order, you can use the +order+ method. @@ -514,6 +521,13 @@ Client.order("orders_count ASC, created_at DESC") Client.order("orders_count ASC", "created_at DESC") </ruby> +If you want to call +order+ multiple times e.g. in different context, new order will prepend previous one + +<ruby> +Client.order("orders_count ASC").order("created_at DESC") +# SELECT * FROM clients ORDER BY created_at DESC, orders_count ASC +</ruby> + h3. Selecting Specific Fields By default, <tt>Model.find</tt> selects all the fields from the result set using +select *+. @@ -1039,7 +1053,7 @@ Even though Active Record lets you specify conditions on the eager loaded associ However if you must do this, you may use +where+ as you would normally. <ruby> -Post.includes(:comments).where("comments.visible", true) +Post.includes(:comments).where("comments.visible" => true) </ruby> This would generate a query which contains a +LEFT OUTER JOIN+ whereas the +joins+ method would generate one using the +INNER JOIN+ function instead. @@ -1128,24 +1142,10 @@ Using a class method is the preferred way to accept arguments for scopes. These category.posts.created_before(time) </ruby> -h4. Working with scopes - -Where a relational object is required, the +scoped+ method may come in handy. This will return an +ActiveRecord::Relation+ object which can have further scoping applied to it afterwards. A place where this may come in handy is on associations - -<ruby> -client = Client.find_by_first_name("Ryan") -orders = client.orders.scoped -</ruby> - -With this new +orders+ object, we are able to ascertain that this object can have more scopes applied to it. For instance, if we wanted to return orders only in the last 30 days at a later point. - -<ruby> -orders.where("created_at > ?", 30.days.ago) -</ruby> - h4. Applying a default scope -If we wish for a scope to be applied across all queries to the model we can use the +default_scope+ method within the model itself. +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. <ruby> class Client < ActiveRecord::Base @@ -1153,15 +1153,29 @@ class Client < ActiveRecord::Base end </ruby> -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: <sql> SELECT * FROM clients WHERE removed_at IS NULL </sql> +If you need to do more complex things with a default scope, you can alternatively +define it as a class method: + +<ruby> +class Client < ActiveRecord::Base + def self.default_scope + # Should return an ActiveRecord::Relation. + end +end +</ruby> + 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. <ruby> Client.unscoped.all @@ -1169,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+: + +<ruby> +Client.unscoped { + Client.created_before(Time.zome.now) +} +</ruby> + 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. @@ -1219,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/active_record_validations_callbacks.textile b/guides/source/active_record_validations_callbacks.textile index f49d91fd3c..b866337e3f 100644 --- a/guides/source/active_record_validations_callbacks.textile +++ b/guides/source/active_record_validations_callbacks.textile @@ -86,6 +86,7 @@ The following methods skip validations, and will save the object to the database * +update_all+ * +update_attribute+ * +update_column+ +* +update_columns+ * +update_counters+ Note that +save+ also has the ability to skip validations if passed +:validate => false+ as argument. This technique should be used with caution. @@ -373,6 +374,8 @@ class LineItem < ActiveRecord::Base end </ruby> +If you validate the presence of an object associated via a +has_one+ or +has_many+ relationship, it will check that the object is neither +blank?+ nor +marked_for_destruction?+. + Since +false.blank?+ is true, if you want to validate the presence of a boolean field you should use <tt>validates :field_name, :inclusion => { :in => [true, false] }</tt>. The default error message is "_can't be empty_". @@ -529,6 +532,16 @@ end Person.new.valid? => ActiveModel::StrictValidationFailed: Name can't be blank </ruby> +There is also an ability to pass custom exception to +:strict+ option + +<ruby> +class Person < ActiveRecord::Base + validates :token, :presence => true, :uniqueness => true, :strict => TokenGenerationException +end + +Person.new.valid? => TokenGenerationException: Token can't be blank +</ruby> + h3. Conditional Validation Sometimes it will make sense to validate an object just when a given predicate is satisfied. You can do that by using the +:if+ and +:unless+ options, which can take a symbol, a string, a +Proc+ or an +Array+. You may use the +:if+ option when you want to specify when the validation *should* happen. If you want to specify when the validation *should not* happen, then you may use the +:unless+ option. @@ -1082,6 +1095,7 @@ Just as with validations, it is also possible to skip callbacks. These methods s * +toggle+ * +touch+ * +update_column+ +* +update_columns+ * +update_all+ * +update_counters+ diff --git a/guides/source/active_support_core_extensions.textile b/guides/source/active_support_core_extensions.textile index 99b8bdd3fd..8748817eb8 100644 --- a/guides/source/active_support_core_extensions.textile +++ b/guides/source/active_support_core_extensions.textile @@ -1325,6 +1325,41 @@ that amount of leading whitespace. NOTE: Defined in +active_support/core_ext/string/strip.rb+. +h4. +indent+ + +Indents the lines in the receiver: + +<ruby> +<<EOS.indent(2) +def some_method + some_code +end +EOS +# => + def some_method + some_code + end +</ruby> + +The second argument, +indent_string+, specifies which indent string to use. The default is +nil+, which tells the method to make an educated guess peeking at the first indented line, and fallback to a space if there is none. + +<ruby> +" foo".indent(2) # => " foo" +"foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar" +"foo".indent(2, "\t") # => "\t\tfoo" +</ruby> + +While +indent_string+ is tipically one space or tab, it may be any string. + +The third argument, +indent_empty_lines+, is a flag that says whether empty lines should be indented. Default is false. + +<ruby> +"foo\n\nbar".indent(2) # => " foo\n\n bar" +"foo\n\nbar".indent(2, nil, true) # => " foo\n \n bar" +</ruby> + +The +indent!+ method performs indentation in-place. + h4. Access h5. +at(position)+ @@ -1456,13 +1491,9 @@ For example, Action Pack uses this method to load the class that provides a cert <ruby> # action_controller/metal/session_management.rb def session_store=(store) - if store == :active_record_store - self.session_store = ActiveRecord::SessionStore - else - @@session_store = store.is_a?(Symbol) ? - ActionDispatch::Session.const_get(store.to_s.camelize) : - store - end + @@session_store = store.is_a?(Symbol) ? + ActionDispatch::Session.const_get(store.to_s.camelize) : + store end </ruby> @@ -2783,27 +2814,7 @@ The method +assert_valid_keys+ receives an arbitrary number of arguments, and ch {:a => 1}.assert_valid_keys("a") # ArgumentError </ruby> -Active Record does not accept unknown options when building associations for example. It implements that control via +assert_valid_keys+: - -<ruby> -mattr_accessor :valid_keys_for_has_many_association -@@valid_keys_for_has_many_association = [ - :class_name, :table_name, :foreign_key, :primary_key, - :dependent, - :select, :conditions, :include, :order, :group, :having, :limit, :offset, - :as, :through, :source, :source_type, - :uniq, - :finder_sql, :counter_sql, - :before_add, :after_add, :before_remove, :after_remove, - :extend, :readonly, - :validate, :inverse_of -] - -def create_has_many_reflection(association_id, options, &extension) - options.assert_valid_keys(valid_keys_for_has_many_association) - ... -end -</ruby> +Active Record does not accept unknown options when building associations, for example. It implements that control via +assert_valid_keys+. NOTE: Defined in +active_support/core_ext/hash/keys.rb+. diff --git a/guides/source/ajax_on_rails.textile b/guides/source/ajax_on_rails.textile index e23fdf9a74..67b0c9f0d3 100644 --- a/guides/source/ajax_on_rails.textile +++ b/guides/source/ajax_on_rails.textile @@ -28,7 +28,7 @@ the +ul+ node. h4. Asynchronous JavaScript + XML AJAX means Asynchronous JavaScript + XML. Asynchronous means that the page is not -reloaded, the request made is separate from the regular page request. Javascript +reloaded, the request made is separate from the regular page request. JavaScript is used to evaluate the response and the XML part is a bit misleading as XML is not required, you respond to the HTTP request with JSON or regular HTML as well. @@ -129,7 +129,7 @@ will produce <ruby> button_to 'Delete Image', { action: 'delete', id: @image.id }, - confirm: 'Are you sure?', method: :delete + method: :delete, data: { confirm: 'Are you sure?' } </ruby> will produce @@ -144,8 +144,8 @@ will produce </html> <ruby> -button_to 'Destroy', 'http://www.example.com', confirm: 'Are you sure?', - method: 'delete', remote: true, data: { disable_with: 'loading...' } +button_to 'Destroy', 'http://www.example.com', + method: 'delete', remote: true, data: { disable_with: 'loading...', confirm: 'Are you sure?' } </ruby> will produce @@ -217,7 +217,6 @@ link_to_remote "Delete the item", Note that if we wouldn't override the default behavior (POST), the above snippet would route to the create action rather than destroy. ** *JavaScript filters* You can customize the remote call further by wrapping it with some JavaScript code. Let's say in the previous example, when deleting a link, you'd like to ask for a confirmation by showing a simple modal text box to the user. This is a typical example what you can accomplish with these options - let's see them one by one: -*** +:confirm+ => +msg+ Pops up a JavaScript confirmation dialog, displaying +msg+. If the user chooses 'OK', the request is launched, otherwise canceled. *** +:condition+ => +code+ Evaluates +code+ (which should evaluate to a boolean) and proceeds if it's true, cancels the request otherwise. *** +:before+ => +code+ Evaluates the +code+ just before launching the request. The output of the code has no influence on the execution. Typically used show a progress indicator (see this in action in the next example). *** +:after+ => +code+ Evaluates the +code+ after launching the request. Note that this is different from the +:success+ or +:complete+ callback (covered in the next section) since those are triggered after the request is completed, while the code snippet passed to +:after+ is evaluated after the remote call is made. A common example is to disable elements on the page or otherwise prevent further action while the request is completed. @@ -307,4 +306,4 @@ JavaScript testing reminds me the definition of the world 'classic' by Mark Twai * Cucumber+Webrat * Mention stuff like screw.unit/jsSpec -Note to self: check out the RailsConf JS testing video
\ No newline at end of file +Note to self: check out the RailsConf JS testing video diff --git a/guides/source/api_documentation_guidelines.textile b/guides/source/api_documentation_guidelines.textile index c6aa1f0a2b..3de5b119ee 100644 --- a/guides/source/api_documentation_guidelines.textile +++ b/guides/source/api_documentation_guidelines.textile @@ -127,7 +127,7 @@ class Array end </ruby> -WARNING: Using a pair of ++...++ for fixed-width font only works with *words*; that is: anything matching <tt>\A\w+\z</tt>. For anything else use +<tt>...</tt>+, notably symbols, setters, inline snippets, etc: +WARNING: Using a pair of ++...++ for fixed-width font only works with *words*; that is: anything matching <tt>\A\w+\z</tt>. For anything else use +<tt>...</tt>+, notably symbols, setters, inline snippets, etc. h4. Regular Font diff --git a/guides/source/asset_pipeline.textile b/guides/source/asset_pipeline.textile index d0952fcf29..2a15e95282 100644 --- a/guides/source/asset_pipeline.textile +++ b/guides/source/asset_pipeline.textile @@ -70,11 +70,11 @@ The query string strategy has several disadvantages: <ol> <li> - <strong>Not all caches will reliably cache content where the filename only differs by query parameters</strong>.<br> + <strong>Not all caches will reliably cache content where the filename only differs by query parameters</strong>.<br /> "Steve Souders recommends":http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/, "...avoiding a querystring for cacheable resources". He found that in this case 5-20% of requests will not be cached. Query strings in particular do not work at all with some CDNs for cache invalidation. </li> <li> - <strong>The file name can change between nodes in multi-server environments.</strong><br> + <strong>The file name can change between nodes in multi-server environments.</strong><br /> The default query string in Rails 2.x is based on the modification time of the files. When assets are deployed to a cluster, there is no guarantee that the timestamps will be the same, resulting in different values being used depending on which server handles the request. </li> <li> @@ -121,7 +121,7 @@ h5. Search paths When a file is referenced from a manifest or a helper, Sprockets searches the three default asset locations for it. -The default locations are: +app/assets/images+ and the subdirectories +javascripts+ and +stylesheets+ in all three asset locations. +The default locations are: +app/assets/images+ and the subdirectories +javascripts+ and +stylesheets+ in all three asset locations, but these subdirectories are not special. Any path under +assets/*+ will be searched. For example, these files: @@ -129,6 +129,7 @@ For example, these files: app/assets/javascripts/home.js lib/assets/javascripts/moovinator.js vendor/assets/javascripts/slider.js +vendor/assets/somepackage/phonebox.js </plain> would be referenced in a manifest like this: @@ -137,6 +138,7 @@ would be referenced in a manifest like this: //= require home //= require moovinator //= require slider +//= require phonebox </plain> Assets inside subdirectories can also be accessed. @@ -153,13 +155,13 @@ is referenced as: You can view the search path by inspecting +Rails.application.config.assets.paths+ in the Rails console. -Additional (fully qualified) paths can be added to the pipeline in +config/application.rb+. For example: +Besides the standard +assets/*+ paths, additional (fully qualified) paths can be added to the pipeline in +config/application.rb+. For example: <ruby> -config.assets.paths << Rails.root.join("app", "assets", "flash") +config.assets.paths << Rails.root.join("lib", "videoplayer", "flash") </ruby> -Paths are traversed in the order that they occur in the search path. +Paths are traversed in the order that they occur in the search path. By default, this means the files in +app/assets+ take precedence, and will mask corresponding paths in +lib+ and +vendor+. It is important to note that files you want to reference outside a manifest must be added to the precompile array or they will not be available in the production environment. @@ -430,7 +432,7 @@ NOTE. If you are precompiling your assets locally, you can use +bundle install - The default matcher for compiling files includes +application.js+, +application.css+ and all non-JS/CSS files (this will include all image assets automatically): <ruby> -[ Proc.new{ |path| !File.extname(path).in?(['.js', '.css']) }, /application.(css|js)$/ ] +[ Proc.new{ |path| !%w(.js .css).include?(File.extname(path)) }, /application.(css|js)$/ ] </ruby> NOTE. The matcher (and other members of the precompile array; see below) is applied to final compiled file names. This means that anything that compiles to JS/CSS is excluded, as well as raw JS/CSS files; for example, +.coffee+ and +.scss+ files are *not* automatically included as they compile to JS/CSS. @@ -441,6 +443,8 @@ If you have other manifests or individual stylesheets and JavaScript files to in config.assets.precompile += ['admin.js', 'admin.css', 'swfObject.js'] </erb> +NOTE. Always specify an expected compiled filename that ends with js or css, even if you want to add Sass or CoffeeScript files to the precompile array. + The rake task also generates a +manifest.yml+ that contains a list with all your assets and their respective fingerprints. This is used by the Rails helper methods to avoid handing the mapping requests back to Sprockets. A typical manifest file looks like: <plain> @@ -469,6 +473,7 @@ Precompiled assets exist on the filesystem and are served directly by your web s For Apache: <plain> +# The Expires* directives requires the Apache module +mod_expires+ to be enabled. <LocationMatch "^/assets/.*$"> # Use of ETag is discouraged when Last-Modified is present Header unset ETag @@ -651,7 +656,7 @@ WARNING: If you are upgrading an existing application and intend to use this opt h3. Assets Cache Store -Sprockets uses the default Rails cache store will be used to cache assets in development and production. This can be changed by setting +config.assets.cache_store+. +The default Rails cache store will be used by Sprockets to cache assets in development and production. This can be changed by setting +config.assets.cache_store+. <ruby> config.assets.cache_store = :memory_store diff --git a/guides/source/association_basics.textile b/guides/source/association_basics.textile index 8ddc56bef1..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: @@ -630,12 +632,12 @@ The <tt>create_<em>association</em></tt> method returns a new object of the asso h5. Options for +belongs_to+ -While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the +belongs_to+ association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this assocation uses two such options: +While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the +belongs_to+ association reference. Such customizations can easily be accomplished by passing options and scope blocks when you create the association. For example, this assocation uses two such options: <ruby> class Order < ActiveRecord::Base - belongs_to :customer, :counter_cache => true, - :conditions => "active = 1" + belongs_to :customer, :dependent => :destroy, + :counter_cache => true end </ruby> @@ -643,15 +645,11 @@ The +belongs_to+ association supports these options: * +:autosave+ * +:class_name+ -* +:conditions+ * +:counter_cache+ * +:dependent+ * +:foreign_key+ -* +:include+ * +:inverse_of+ * +:polymorphic+ -* +:readonly+ -* +:select+ * +:touch+ * +:validate+ @@ -669,16 +667,6 @@ class Order < ActiveRecord::Base end </ruby> -h6(#belongs_to-conditions). +:conditions+ - -The +:conditions+ option lets you specify the conditions that the associated object must meet (in the syntax used by an SQL +WHERE+ clause). - -<ruby> -class Order < ActiveRecord::Base - belongs_to :customer, :conditions => "active = 1" -end -</ruby> - h6(#belongs_to-counter_cache). +:counter_cache+ The +:counter_cache+ option can be used to make finding the number of belonging objects more efficient. Consider these models: @@ -737,35 +725,31 @@ end TIP: In any case, Rails will not create foreign key columns for you. You need to explicitly define them as part of your migrations. -h6(#belongs_to-includes). +:include+ +h6(#belongs_to-inverse_of). +:inverse_of+ -You can use the +:include+ option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models: +The +:inverse_of+ option specifies the name of the +has_many+ or +has_one+ association that is the inverse of this association. Does not work in combination with the +:polymorphic+ options. <ruby> -class LineItem < ActiveRecord::Base - belongs_to :order +class Customer < ActiveRecord::Base + has_many :orders, :inverse_of => :customer end class Order < ActiveRecord::Base - belongs_to :customer - has_many :line_items -end - -class Customer < ActiveRecord::Base - has_many :orders + belongs_to :customer, :inverse_of => :orders end </ruby> -If you frequently retrieve customers directly from line items (+@line_item.order.customer+), then you can make your code somewhat more efficient by including customers in the association from line items to orders: +h6(#belongs_to-polymorphic). +:polymorphic+ -<ruby> -class LineItem < ActiveRecord::Base - belongs_to :order, :include => :customer -end +Passing +true+ to the +:polymorphic+ option indicates that this is a polymorphic association. Polymorphic associations were discussed in detail <a href="#polymorphic-associations">earlier in this guide</a>. + +h6(#belongs_to-touch). +:touch+ + +If you set the +:touch+ option to +:true+, then the +updated_at+ or +updated_on+ timestamp on the associated object will be set to the current time whenever this object is saved or destroyed: +<ruby> class Order < ActiveRecord::Base - belongs_to :customer - has_many :line_items + belongs_to :customer, :touch => true end class Customer < ActiveRecord::Base @@ -773,43 +757,58 @@ class Customer < ActiveRecord::Base end </ruby> -NOTE: There's no need to use +:include+ for immediate associations - that is, if you have +Order belongs_to :customer+, then the customer is eager-loaded automatically when it's needed. - -h6(#belongs_to-inverse_of). +:inverse_of+ - -The +:inverse_of+ option specifies the name of the +has_many+ or +has_one+ association that is the inverse of this association. Does not work in combination with the +:polymorphic+ options. +In this case, saving or destroying an order will update the timestamp on the associated customer. You can also specify a particular timestamp attribute to update: <ruby> -class Customer < ActiveRecord::Base - has_many :orders, :inverse_of => :customer -end - class Order < ActiveRecord::Base - belongs_to :customer, :inverse_of => :orders + belongs_to :customer, :touch => :orders_updated_at end </ruby> -h6(#belongs_to-polymorphic). +:polymorphic+ +h6(#belongs_to-validate). +:validate+ -Passing +true+ to the +:polymorphic+ option indicates that this is a polymorphic association. Polymorphic associations were discussed in detail <a href="#polymorphic-associations">earlier in this guide</a>. +If you set the +:validate+ option to +true+, then associated objects will be validated whenever you save this object. By default, this is +false+: associated objects will not be validated when this object is saved. -h6(#belongs_to-readonly). +:readonly+ +h5(#belongs_to-scopes_for_belongs_to). Scopes for +belongs_to+ -If you set the +:readonly+ option to +true+, then the associated object will be read-only when retrieved via the association. +There may be times when you wish to customize the query used by +belongs_to+. Such customizations can be achieved via a scope block. For example: -h6(#belongs_to-select). +:select+ +<ruby> +class Order < ActiveRecord::Base + belongs_to :customer, -> { where :active => true }, + :dependent => :destroy +end +</ruby> -The +:select+ option lets you override the SQL +SELECT+ clause that is used to retrieve data about the associated object. By default, Rails retrieves all columns. +You can use any of the standard "querying methods":active_record_querying.html inside the scope block. The following ones are discussed below: -TIP: If you set the +:select+ option on a +belongs_to+ association, you should also set the +foreign_key+ option to guarantee the correct results. +* +where+ +* +includes+ +* +readonly+ +* +select+ -h6(#belongs_to-touch). +:touch+ +h6(#belongs_to-where). +where+ -If you set the +:touch+ option to +:true+, then the +updated_at+ or +updated_on+ timestamp on the associated object will be set to the current time whenever this object is saved or destroyed: +The +where+ method lets you specify the conditions that the associated object must meet. <ruby> class Order < ActiveRecord::Base - belongs_to :customer, :touch => true + belongs_to :customer, -> { where :active => true } +end +</ruby> + +h6(#belongs_to-includes). +includes+ + +You can use the +includes+ method let you specify second-order associations that should be eager-loaded when this association is used. For example, consider these models: + +<ruby> +class LineItem < ActiveRecord::Base + belongs_to :order +end + +class Order < ActiveRecord::Base + belongs_to :customer + has_many :line_items end class Customer < ActiveRecord::Base @@ -817,17 +816,34 @@ class Customer < ActiveRecord::Base end </ruby> -In this case, saving or destroying an order will update the timestamp on the associated customer. You can also specify a particular timestamp attribute to update: +If you frequently retrieve customers directly from line items (+@line_item.order.customer+), then you can make your code somewhat more efficient by including customers in the association from line items to orders: <ruby> +class LineItem < ActiveRecord::Base + belongs_to :order, -> { includes :customer } +end + class Order < ActiveRecord::Base - belongs_to :customer, :touch => :orders_updated_at + belongs_to :customer + has_many :line_items +end + +class Customer < ActiveRecord::Base + has_many :orders end </ruby> -h6(#belongs_to-validate). +:validate+ +NOTE: There's no need to use +includes+ for immediate associations - that is, if you have +Order belongs_to :customer+, then the customer is eager-loaded automatically when it's needed. -If you set the +:validate+ option to +true+, then associated objects will be validated whenever you save this object. By default, this is +false+: associated objects will not be validated when this object is saved. +h6(#belongs_to-readonly). +readonly+ + +If you use +readonly+, then the associated object will be read-only when retrieved via the association. + +h6(#belongs_to-select). +select+ + +The +select+ method lets you override the SQL +SELECT+ clause that is used to retrieve data about the associated object. By default, Rails retrieves all columns. + +TIP: If you use the +select+ method on a +belongs_to+ association, you should also set the +:foreign_key+ option to guarantee the correct results. h5(#belongs_to-do_any_associated_objects_exist). Do Any Associated Objects Exist? @@ -924,15 +940,10 @@ The +has_one+ association supports these options: * +:as+ * +:autosave+ * +:class_name+ -* +:conditions+ * +:dependent+ * +:foreign_key+ -* +:include+ * +:inverse_of+ -* +:order+ * +:primary_key+ -* +:readonly+ -* +:select+ * +:source+ * +:source_type+ * +:through+ @@ -956,22 +967,15 @@ class Supplier < ActiveRecord::Base end </ruby> -h6(#has_one-conditions). +:conditions+ - -The +:conditions+ option lets you specify the conditions that the associated object must meet (in the syntax used by an SQL +WHERE+ clause). - -<ruby> -class Supplier < ActiveRecord::Base - has_one :account, :conditions => "confirmed = 1" -end -</ruby> - h6(#has_one-dependent). +:dependent+ -If you set the +:dependent+ option to +:destroy+, then deleting this object will call the +destroy+ method on the associated object to delete that object. If you set the +:dependent+ option to +:delete+, then deleting this object will delete the associated object _without_ calling its +destroy+ method. If you set the +:dependent+ option to +:nullify+, then deleting this object will set the foreign key in the association object to +NULL+. -If you set the +:dependent+ option to +:restrict+, then the deletion of the object is restricted if a dependent associated object exist and a +DeleteRestrictionError+ exception is raised. +Controls what happens to the associated object when its owner is destroyed: -NOTE: The default behavior for +:dependent => :restrict+ is to raise a +DeleteRestrictionError+ when associated objects exist. Since Rails 4.0 this behavior is being deprecated in favor of adding an error to the base model. To silence the warning in Rails 4.0, you should fix your code to not expect this Exception and add +config.active_record.dependent_restrict_raises = false+ to your application config. +* +:destroy+ causes the associated object to also be destroyed +* +:delete+ causes the asssociated object to be deleted directly from the database (so callbacks will not execute) +* +:nullify+ causes the foreign key to be set to +NULL+. Callbacks are not executed. +* +:restrict_with_exception+ causes an exception to be raised if there is an associated record +* +:restrict_with_error+ causes an error to be added to the owner if there is an associated object h6(#has_one-foreign_key). +:foreign_key+ @@ -985,30 +989,74 @@ end TIP: In any case, Rails will not create foreign key columns for you. You need to explicitly define them as part of your migrations. -h6(#has_one-include). +:include+ +h6(#has_one-inverse_of). +:inverse_of+ -You can use the +:include+ option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models: +The +:inverse_of+ option specifies the name of the +belongs_to+ association that is the inverse of this association. Does not work in combination with the +:through+ or +:as+ options. <ruby> class Supplier < ActiveRecord::Base - has_one :account + has_one :account, :inverse_of => :supplier end class Account < ActiveRecord::Base - belongs_to :supplier - belongs_to :representative + belongs_to :supplier, :inverse_of => :account end +</ruby> -class Representative < ActiveRecord::Base - has_many :accounts +h6(#has_one-primary_key). +:primary_key+ + +By convention, Rails assumes that the column used to hold the primary key of this model is +id+. You can override this and explicitly specify the primary key with the +:primary_key+ option. + +h6(#has_one-source). +:source+ + +The +:source+ option specifies the source association name for a +has_one :through+ association. + +h6(#has_one-source_type). +:source_type+ + +The +:source_type+ option specifies the source association type for a +has_one :through+ association that proceeds through a polymorphic association. + +h6(#has_one-through). +:through+ + +The +:through+ option specifies a join model through which to perform the query. +has_one :through+ associations were discussed in detail <a href="#the-has_one-through-association">earlier in this guide</a>. + +h6(#has_one-validate). +:validate+ + +If you set the +:validate+ option to +true+, then associated objects will be validated whenever you save this object. By default, this is +false+: associated objects will not be validated when this object is saved. + +h5(#belongs_to-scopes_for_has_one). Scopes for +has_one+ + +There may be times when you wish to customize the query used by +has_one+. Such customizations can be achieved via a scope block. For example: + +<ruby> +class Supplier < ActiveRecord::Base + has_one :account, -> { where :active => true } end </ruby> -If you frequently retrieve representatives directly from suppliers (+@supplier.account.representative+), then you can make your code somewhat more efficient by including representatives in the association from suppliers to accounts: +You can use any of the standard "querying methods":active_record_querying.html inside the scope block. The following ones are discussed below: + +* +where+ +* +includes+ +* +readonly+ +* +select+ + +h6(#has_one-where). +where+ + +The +where+ method lets you specify the conditions that the associated object must meet. + +<ruby> +class Supplier < ActiveRecord::Base + has_one :account, -> { where "confirmed = 1" } +end +</ruby> + +h6(#has_one-includes). +includes+ + +You can use the +includes+ method to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models: <ruby> class Supplier < ActiveRecord::Base - has_one :account, :include => :representative + has_one :account end class Account < ActiveRecord::Base @@ -1021,51 +1069,30 @@ class Representative < ActiveRecord::Base end </ruby> -h6(#has_one-inverse_of). +:inverse_of+ - -The +:inverse_of+ option specifies the name of the +belongs_to+ association that is the inverse of this association. Does not work in combination with the +:through+ or +:as+ options. +If you frequently retrieve representatives directly from suppliers (+@supplier.account.representative+), then you can make your code somewhat more efficient by including representatives in the association from suppliers to accounts: <ruby> class Supplier < ActiveRecord::Base - has_one :account, :inverse_of => :supplier + has_one :account, -> { includes :representative } end class Account < ActiveRecord::Base - belongs_to :supplier, :inverse_of => :account + belongs_to :supplier + belongs_to :representative end -</ruby> - -h6(#has_one-order). +:order+ - -The +:order+ option dictates the order in which associated objects will be received (in the syntax used by an SQL +ORDER BY+ clause). Because a +has_one+ association will only retrieve a single associated object, this option should not be needed. - -h6(#has_one-primary_key). +:primary_key+ - -By convention, Rails assumes that the column used to hold the primary key of this model is +id+. You can override this and explicitly specify the primary key with the +:primary_key+ option. - -h6(#has_one-readonly). +:readonly+ - -If you set the +:readonly+ option to +true+, then the associated object will be read-only when retrieved via the association. - -h6(#has_one-select). +:select+ - -The +:select+ option lets you override the SQL +SELECT+ clause that is used to retrieve data about the associated object. By default, Rails retrieves all columns. -h6(#has_one-source). +:source+ - -The +:source+ option specifies the source association name for a +has_one :through+ association. - -h6(#has_one-source_type). +:source_type+ - -The +:source_type+ option specifies the source association type for a +has_one :through+ association that proceeds through a polymorphic association. +class Representative < ActiveRecord::Base + has_many :accounts +end +</ruby> -h6(#has_one-through). +:through+ +h6(#has_one-readonly). +readonly+ -The +:through+ option specifies a join model through which to perform the query. +has_one :through+ associations were discussed in detail <a href="#the-has_one-through-association">earlier in this guide</a>. +If you use the +readonly+ method, then the associated object will be read-only when retrieved via the association. -h6(#has_one-validate). +:validate+ +h6(#has_one-select). +select+ -If you set the +:validate+ option to +true+, then associated objects will be validated whenever you save this object. By default, this is +false+: associated objects will not be validated when this object is saved. +The +select+ method lets you override the SQL +SELECT+ clause that is used to retrieve data about the associated object. By default, Rails retrieves all columns. h5(#has_one-do_any_associated_objects_exist). Do Any Associated Objects Exist? @@ -1256,25 +1283,13 @@ The +has_many+ association supports these options: * +:as+ * +:autosave+ * +:class_name+ -* +:conditions+ -* +:counter_sql+ * +:dependent+ -* +:extend+ -* +:finder_sql+ * +:foreign_key+ -* +:group+ -* +:include+ * +:inverse_of+ -* +:limit+ -* +:offset+ -* +:order+ * +:primary_key+ -* +:readonly+ -* +:select+ * +:source+ * +:source_type+ * +:through+ -* +:uniq+ * +:validate+ h6(#has_many-as). +:as+ @@ -1295,85 +1310,127 @@ class Customer < ActiveRecord::Base end </ruby> -h6(#has_many-conditions). +:conditions+ +h6(#has_many-dependent). +:dependent+ -The +:conditions+ option lets you specify the conditions that the associated object must meet (in the syntax used by an SQL +WHERE+ clause). +Controls what happens to the associated objects when their owner is destroyed: -<ruby> -class Customer < ActiveRecord::Base - has_many :confirmed_orders, :class_name => "Order", - :conditions => "confirmed = 1" -end -</ruby> +* +:destroy+ causes all the associated objects to also be destroyed +* +:delete_all+ causes all the asssociated objects to be deleted directly from the database (so callbacks will not execute) +* +:nullify+ causes the foreign keys to be set to +NULL+. Callbacks are not executed. +* +:restrict_with_exception+ causes an exception to be raised if there are any associated records +* +:restrict_with_error+ causes an error to be added to the owner if there are any associated objects -You can also set conditions via a hash: +NOTE: This option is ignored when you use the +:through+ option on the association. + +h6(#has_many-foreign_key). +:foreign_key+ + +By convention, Rails assumes that the column used to hold the foreign key on the other model is the name of this model with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly: <ruby> class Customer < ActiveRecord::Base - has_many :confirmed_orders, :class_name => "Order", - :conditions => { :confirmed => true } + has_many :orders, :foreign_key => "cust_id" end </ruby> -If you use a hash-style +:conditions+ option, then record creation via this association will be automatically scoped using the hash. In this case, using +@customer.confirmed_orders.create+ or +@customer.confirmed_orders.build+ will create orders where the confirmed column has the value +true+. +TIP: In any case, Rails will not create foreign key columns for you. You need to explicitly define them as part of your migrations. + +h6(#has_many-inverse_of). +:inverse_of+ -If you need to evaluate conditions dynamically at runtime, use a proc: +The +:inverse_of+ option specifies the name of the +belongs_to+ association that is the inverse of this association. Does not work in combination with the +:through+ or +:as+ options. <ruby> class Customer < ActiveRecord::Base - has_many :latest_orders, :class_name => "Order", - :conditions => proc { ["orders.created_at > ?", 10.hours.ago] } + has_many :orders, :inverse_of => :customer +end + +class Order < ActiveRecord::Base + belongs_to :customer, :inverse_of => :orders end </ruby> -h6(#has_many-counter_sql). +:counter_sql+ +h6(#has_many-primary_key). +:primary_key+ -Normally Rails automatically generates the proper SQL to count the association members. With the +:counter_sql+ option, you can specify a complete SQL statement to count them yourself. +By convention, Rails assumes that the column used to hold the primary key of the association is +id+. You can override this and explicitly specify the primary key with the +:primary_key+ option. -NOTE: If you specify +:finder_sql+ but not +:counter_sql+, then the counter SQL will be generated by substituting the +SELECT ... FROM+ clause of your +:finder_sql+ statement by +SELECT COUNT(*) FROM+. +h6(#has_many-source). +:source+ -h6(#has_many-dependent). +:dependent+ +The +:source+ option specifies the source association name for a +has_many :through+ association. You only need to use this option if the name of the source association cannot be automatically inferred from the association name. -If you set the +:dependent+ option to +:destroy+, then deleting this object will call the +destroy+ method on the associated objects to delete those objects. If you set the +:dependent+ option to +:delete_all+, then deleting this object will delete the associated objects _without_ calling their +destroy+ method. If you set the +:dependent+ option to +:nullify+, then deleting this object will set the foreign key in the associated objects to +NULL+. -If you set the +:dependent+ option to +:restrict+, then the deletion of the object is restricted if a dependent associated object exist and a +DeleteRestrictionError+ exception is raised. +h6(#has_many-source_type). +:source_type+ -NOTE: The default behavior for +:dependent => :restrict+ is to raise a +DeleteRestrictionError+ when associated objects exist. Since Rails 4.0 this behavior is being deprecated in favor of adding an error to the base model. To silence the warning in Rails 4.0, you should fix your code to not expect this Exception and add +config.active_record.dependent_restrict_raises = false+ to your application config. +The +:source_type+ option specifies the source association type for a +has_many :through+ association that proceeds through a polymorphic association. -NOTE: This option is ignored when you use the +:through+ option on the association. +h6(#has_many-through). +:through+ -h6(#has_many-extend). +:extend+ +The +:through+ option specifies a join model through which to perform the query. +has_many :through+ associations provide a way to implement many-to-many relationships, as discussed <a href="#the-has_many-through-association">earlier in this guide</a>. -The +:extend+ option specifies a named module to extend the association proxy. Association extensions are discussed in detail <a href="#association-extensions">later in this guide</a>. +h6(#has_many-validate). +:validate+ -h6(#has_many-finder_sql). +:finder_sql+ +If you set the +:validate+ option to +false+, then associated objects will not be validated whenever you save this object. By default, this is +true+: associated objects will be validated when this object is saved. -Normally Rails automatically generates the proper SQL to fetch the association members. With the +:finder_sql+ option, you can specify a complete SQL statement to fetch them yourself. If fetching objects requires complex multi-table SQL, this may be necessary. +h5(#has_many-scopes_for_has_many). Scopes for +has_many+ -h6(#has_many-foreign_key). +:foreign_key+ +There may be times when you wish to customize the query used by +has_many+. Such customizations can be achieved via a scope block. For example: -By convention, Rails assumes that the column used to hold the foreign key on the other model is the name of this model with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly: +<ruby> +class Customer < ActiveRecord::Base + has_many :orders, -> { where :processed => true } +end +</ruby> + +You can use any of the standard "querying methods":active_record_querying.html inside the scope block. The following ones are discussed below: + +* +where+ +* +extending+ +* +group+ +* +includes+ +* +limit+ +* +offset+ +* +order+ +* +readonly+ +* +select+ +* +uniq+ + +h6(#has_many-where). +where+ + +The +where+ method lets you specify the conditions that the associated object must meet. <ruby> class Customer < ActiveRecord::Base - has_many :orders, :foreign_key => "cust_id" + has_many :confirmed_orders, -> { where "confirmed = 1" }, + :class_name => "Order" end </ruby> -TIP: In any case, Rails will not create foreign key columns for you. You need to explicitly define them as part of your migrations. +You can also set conditions via a hash: + +<ruby> +class Customer < ActiveRecord::Base + has_many :confirmed_orders, -> { where :confirmed => true }, + :class_name => "Order" +end +</ruby> + +If you use a hash-style +where+ option, then record creation via this association will be automatically scoped using the hash. In this case, using +@customer.confirmed_orders.create+ or +@customer.confirmed_orders.build+ will create orders where the confirmed column has the value +true+. + +h6(#has_many-extending). +extending+ -h6(#has_many-group). +:group+ +The +extending+ method specifies a named module to extend the association proxy. Association extensions are discussed in detail <a href="#association-extensions">later in this guide</a>. -The +:group+ option supplies an attribute name to group the result set by, using a +GROUP BY+ clause in the finder SQL. +h6(#has_many-group). +group+ + +The +group+ method supplies an attribute name to group the result set by, using a +GROUP BY+ clause in the finder SQL. <ruby> class Customer < ActiveRecord::Base - has_many :line_items, :through => :orders, :group => "orders.id" + has_many :line_items, -> { group 'orders.id' }, + :through => :orders end </ruby> -h6(#has_many-include). +:include+ +h6(#has_many-includes). +includes+ -You can use the +:include+ option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models: +You can use the +includes+ method to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models: <ruby> class Customer < ActiveRecord::Base @@ -1394,7 +1451,7 @@ If you frequently retrieve line items directly from customers (+@customer.orders <ruby> class Customer < ActiveRecord::Base - has_many :orders, :include => :line_items + has_many :orders, -> { includes :line_items } end class Order < ActiveRecord::Base @@ -1407,74 +1464,45 @@ class LineItem < ActiveRecord::Base end </ruby> -h6(#has_many-inverse_of). +:inverse_of+ - -The +:inverse_of+ option specifies the name of the +belongs_to+ association that is the inverse of this association. Does not work in combination with the +:through+ or +:as+ options. - -<ruby> -class Customer < ActiveRecord::Base - has_many :orders, :inverse_of => :customer -end - -class Order < ActiveRecord::Base - belongs_to :customer, :inverse_of => :orders -end -</ruby> - -h6(#has_many-limit). +:limit+ +h6(#has_many-limit). +limit+ -The +:limit+ option lets you restrict the total number of objects that will be fetched through an association. +The +limit+ method lets you restrict the total number of objects that will be fetched through an association. <ruby> class Customer < ActiveRecord::Base - has_many :recent_orders, :class_name => "Order", - :order => "order_date DESC", :limit => 100 + has_many :recent_orders, + -> { order('order_date desc').limit(100) }, + :class_name => "Order", end </ruby> -h6(#has_many-offset). +:offset+ +h6(#has_many-offset). +offset+ -The +:offset+ option lets you specify the starting offset for fetching objects via an association. For example, if you set +:offset => 11+, it will skip the first 11 records. +The +offset+ method lets you specify the starting offset for fetching objects via an association. For example, +-> { offset(11) }+ will skip the first 11 records. -h6(#has_many-order). +:order+ +h6(#has_many-order). +order+ -The +:order+ option dictates the order in which associated objects will be received (in the syntax used by an SQL +ORDER BY+ clause). +The +order+ method dictates the order in which associated objects will be received (in the syntax used by an SQL +ORDER BY+ clause). <ruby> class Customer < ActiveRecord::Base - has_many :orders, :order => "date_confirmed DESC" + has_many :orders, -> { order "date_confirmed DESC" } end </ruby> -h6(#has_many-primary_key). +:primary_key+ - -By convention, Rails assumes that the column used to hold the primary key of the association is +id+. You can override this and explicitly specify the primary key with the +:primary_key+ option. - -h6(#has_many-readonly). +:readonly+ - -If you set the +:readonly+ option to +true+, then the associated objects will be read-only when retrieved via the association. - -h6(#has_many-select). +:select+ - -The +:select+ option lets you override the SQL +SELECT+ clause that is used to retrieve data about the associated objects. By default, Rails retrieves all columns. +h6(#has_many-readonly). +readonly+ -WARNING: If you specify your own +:select+, be sure to include the primary key and foreign key columns of the associated model. If you do not, Rails will throw an error. +If you use the +readonly+ method, then the associated objects will be read-only when retrieved via the association. -h6(#has_many-source). +:source+ - -The +:source+ option specifies the source association name for a +has_many :through+ association. You only need to use this option if the name of the source association cannot be automatically inferred from the association name. - -h6(#has_many-source_type). +:source_type+ +h6(#has_many-select). +select+ -The +:source_type+ option specifies the source association type for a +has_many :through+ association that proceeds through a polymorphic association. +The +select+ method lets you override the SQL +SELECT+ clause that is used to retrieve data about the associated objects. By default, Rails retrieves all columns. -h6(#has_many-through). +:through+ +WARNING: If you specify your own +select+, be sure to include the primary key and foreign key columns of the associated model. If you do not, Rails will throw an error. -The +:through+ option specifies a join model through which to perform the query. +has_many :through+ associations provide a way to implement many-to-many relationships, as discussed <a href="#the-has_many-through-association">earlier in this guide</a>. +h6(#has_many-uniq). +uniq+ -h6(#has_many-uniq). +:uniq+ - -Set the +:uniq+ option to true to keep the collection free of duplicates. This is mostly useful together with the +:through+ option. +Use the +uniq+ method to keep the collection free of duplicates. This is mostly useful together with the +:through+ option. <ruby> class Person < ActiveRecord::Base @@ -1492,12 +1520,12 @@ Reading.all.inspect # => [#<Reading id: 12, person_id: 5, post_id: 5>, #<Readin In the above case there are two readings and +person.posts+ brings out both of them even though these records are pointing to the same post. -Now let's set +:uniq+ to true: +Now let's set +uniq+: <ruby> class Person has_many :readings - has_many :posts, :through => :readings, :uniq => true + has_many :posts, -> { uniq }, :through => :readings end person = Person.create(:name => 'honda') @@ -1510,10 +1538,6 @@ Reading.all.inspect # => [#<Reading id: 16, person_id: 7, post_id: 7>, #<Readin In the above case there are still two readings. However +person.posts+ shows only one post because the collection loads only unique records. -h6(#has_many-validate). +:validate+ - -If you set the +:validate+ option to +false+, then associated objects will not be validated whenever you save this object. By default, this is +true+: associated objects will be validated when this object is saved. - h5(#has_many-when_are_objects_saved). When are Objects Saved? When you assign an object to a +has_many+ association, that object is automatically saved (in order to update its foreign key). If you assign multiple objects in one statement, then they are all saved. @@ -1699,22 +1723,8 @@ The +has_and_belongs_to_many+ association supports these options: * +:association_foreign_key+ * +:autosave+ * +:class_name+ -* +:conditions+ -* +:counter_sql+ -* +:delete_sql+ -* +:extend+ -* +:finder_sql+ * +:foreign_key+ -* +:group+ -* +:include+ -* +:insert_sql+ * +:join_table+ -* +:limit+ -* +:offset+ -* +:order+ -* +:readonly+ -* +:select+ -* +:uniq+ * +:validate+ h6(#has_and_belongs_to_many-association_foreign_key). +:association_foreign_key+ @@ -1745,120 +1755,126 @@ class Parts < ActiveRecord::Base end </ruby> -h6(#has_and_belongs_to_many-conditions). +:conditions+ - -The +:conditions+ option lets you specify the conditions that the associated object must meet (in the syntax used by an SQL +WHERE+ clause). - -<ruby> -class Parts < ActiveRecord::Base - has_and_belongs_to_many :assemblies, - :conditions => "factory = 'Seattle'" -end -</ruby> +h6(#has_and_belongs_to_many-foreign_key). +:foreign_key+ -You can also set conditions via a hash: +By convention, Rails assumes that the column in the join table used to hold the foreign key pointing to this model is the name of this model with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly: <ruby> -class Parts < ActiveRecord::Base - has_and_belongs_to_many :assemblies, - :conditions => { :factory => 'Seattle' } +class User < ActiveRecord::Base + has_and_belongs_to_many :friends, :class_name => "User", + :foreign_key => "this_user_id", + :association_foreign_key => "other_user_id" end </ruby> -If you use a hash-style +:conditions+ option, then record creation via this association will be automatically scoped using the hash. In this case, using +@parts.assemblies.create+ or +@parts.assemblies.build+ will create orders where the +factory+ column has the value "Seattle". - -h6(#has_and_belongs_to_many-counter_sql). +:counter_sql+ +h6(#has_and_belongs_to_many-join_table). +:join_table+ -Normally Rails automatically generates the proper SQL to count the association members. With the +:counter_sql+ option, you can specify a complete SQL statement to count them yourself. +If the default name of the join table, based on lexical ordering, is not what you want, you can use the +:join_table+ option to override the default. -NOTE: If you specify +:finder_sql+ but not +:counter_sql+, then the counter SQL will be generated by substituting the +SELECT ... FROM+ clause of your +:finder_sql+ statement by +SELECT COUNT(*) FROM+. +h6(#has_and_belongs_to_many-validate). +:validate+ -h6(#has_and_belongs_to_many-delete_sql). +:delete_sql+ +If you set the +:validate+ option to +false+, then associated objects will not be validated whenever you save this object. By default, this is +true+: associated objects will be validated when this object is saved. -Normally Rails automatically generates the proper SQL to remove links between the associated classes. With the +:delete_sql+ option, you can specify a complete SQL statement to delete them yourself. +h5(#has_and_belongs_to_many-scopes_for_has_and_belongs_to_many). Scopes for +has_and_belongs_to_many+ -h6(#has_and_belongs_to_many-extend). +:extend+ +There may be times when you wish to customize the query used by +has_and_belongs_to_many+. Such customizations can be achieved via a scope block. For example: -The +:extend+ option specifies a named module to extend the association proxy. Association extensions are discussed in detail <a href="#association-extensions">later in this guide</a>. +<ruby> +class Parts < ActiveRecord::Base + has_and_belongs_to_many :assemblies, -> { where :active => true } +end +</ruby> -h6(#has_and_belongs_to_many-finder_sql). +:finder_sql+ +You can use any of the standard "querying methods":active_record_querying.html inside the scope block. The following ones are discussed below: -Normally Rails automatically generates the proper SQL to fetch the association members. With the +:finder_sql+ option, you can specify a complete SQL statement to fetch them yourself. If fetching objects requires complex multi-table SQL, this may be necessary. +* +where+ +* +extending+ +* +group+ +* +includes+ +* +limit+ +* +offset+ +* +order+ +* +readonly+ +* +select+ +* +uniq+ -h6(#has_and_belongs_to_many-foreign_key). +:foreign_key+ +h6(#has_and_belongs_to_many-where). +where+ -By convention, Rails assumes that the column in the join table used to hold the foreign key pointing to this model is the name of this model with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly: +The +where+ method lets you specify the conditions that the associated object must meet. <ruby> -class User < ActiveRecord::Base - has_and_belongs_to_many :friends, :class_name => "User", - :foreign_key => "this_user_id", - :association_foreign_key => "other_user_id" +class Parts < ActiveRecord::Base + has_and_belongs_to_many :assemblies, + -> { where "factory = 'Seattle'" } end </ruby> -h6(#has_and_belongs_to_many-group). +:group+ - -The +:group+ option supplies an attribute name to group the result set by, using a +GROUP BY+ clause in the finder SQL. +You can also set conditions via a hash: <ruby> class Parts < ActiveRecord::Base - has_and_belongs_to_many :assemblies, :group => "factory" + has_and_belongs_to_many :assemblies, + -> { where :factory => 'Seattle' } end </ruby> -h6(#has_and_belongs_to_many-include). +:include+ +If you use a hash-style +where+, then record creation via this association will be automatically scoped using the hash. In this case, using +@parts.assemblies.create+ or +@parts.assemblies.build+ will create orders where the +factory+ column has the value "Seattle". -You can use the +:include+ option to specify second-order associations that should be eager-loaded when this association is used. +h6(#has_and_belongs_to_many-extending). +extending+ -h6(#has_and_belongs_to_many-insert_sql). +:insert_sql+ +The +extending+ method specifies a named module to extend the association proxy. Association extensions are discussed in detail <a href="#association-extensions">later in this guide</a>. -Normally Rails automatically generates the proper SQL to create links between the associated classes. With the +:insert_sql+ option, you can specify a complete SQL statement to insert them yourself. +h6(#has_and_belongs_to_many-group). +group+ -h6(#has_and_belongs_to_many-join_table). +:join_table+ +The +group+ method supplies an attribute name to group the result set by, using a +GROUP BY+ clause in the finder SQL. -If the default name of the join table, based on lexical ordering, is not what you want, you can use the +:join_table+ option to override the default. +<ruby> +class Parts < ActiveRecord::Base + has_and_belongs_to_many :assemblies, -> { group "factory" } +end +</ruby> -h6(#has_and_belongs_to_many-limit). +:limit+ +h6(#has_and_belongs_to_many-includes). +includes+ -The +:limit+ option lets you restrict the total number of objects that will be fetched through an association. +You can use the +includes+ method to specify second-order associations that should be eager-loaded when this association is used. + +h6(#has_and_belongs_to_many-limit). +limit+ + +The +limit+ method lets you restrict the total number of objects that will be fetched through an association. <ruby> class Parts < ActiveRecord::Base - has_and_belongs_to_many :assemblies, :order => "created_at DESC", - :limit => 50 + has_and_belongs_to_many :assemblies, + -> { order("created_at DESC").limit(50) } end </ruby> -h6(#has_and_belongs_to_many-offset). +:offset+ +h6(#has_and_belongs_to_many-offset). +offset+ -The +:offset+ option lets you specify the starting offset for fetching objects via an association. For example, if you set +:offset => 11+, it will skip the first 11 records. +The +offset+ method lets you specify the starting offset for fetching objects via an association. For example, if you set +offset(11)+, it will skip the first 11 records. -h6(#has_and_belongs_to_many-order). +:order+ +h6(#has_and_belongs_to_many-order). +order+ -The +:order+ option dictates the order in which associated objects will be received (in the syntax used by an SQL +ORDER BY+ clause). +The +order+ method dictates the order in which associated objects will be received (in the syntax used by an SQL +ORDER BY+ clause). <ruby> class Parts < ActiveRecord::Base - has_and_belongs_to_many :assemblies, :order => "assembly_name ASC" + has_and_belongs_to_many :assemblies, + -> { order "assembly_name ASC" } end </ruby> -h6(#has_and_belongs_to_many-readonly). +:readonly+ - -If you set the +:readonly+ option to +true+, then the associated objects will be read-only when retrieved via the association. +h6(#has_and_belongs_to_many-readonly). +readonly+ -h6(#has_and_belongs_to_many-select). +:select+ +If you use the +readonly+ method, then the associated objects will be read-only when retrieved via the association. -The +:select+ option lets you override the SQL +SELECT+ clause that is used to retrieve data about the associated objects. By default, Rails retrieves all columns. +h6(#has_and_belongs_to_many-select). +select+ -h6(#has_and_belongs_to_many-uniq). +:uniq+ +The +select+ method lets you override the SQL +SELECT+ clause that is used to retrieve data about the associated objects. By default, Rails retrieves all columns. -Specify the +:uniq => true+ option to remove duplicates from the collection. +h6(#has_and_belongs_to_many-uniq). +uniq+ -h6(#has_and_belongs_to_many-validate). +:validate+ - -If you set the +:validate+ option to +false+, then associated objects will not be validated whenever you save this object. By default, this is +true+: associated objects will be validated when this object is saved. +Use the +uniq+ method to remove duplicates from the collection. h5(#has_and_belongs_to_many-when_are_objects_saved). When are Objects Saved? @@ -1938,20 +1954,11 @@ module FindRecentExtension end class Customer < ActiveRecord::Base - has_many :orders, :extend => FindRecentExtension + has_many :orders, -> { extending FindRecentExtension } end class Supplier < ActiveRecord::Base - has_many :deliveries, :extend => FindRecentExtension -end -</ruby> - -To include more than one extension module in a single association, specify an array of modules: - -<ruby> -class Customer < ActiveRecord::Base - has_many :orders, - :extend => [FindRecentExtension, FindActiveExtension] + has_many :deliveries, -> { extending FindRecentExtension } end </ruby> diff --git a/guides/source/caching_with_rails.textile b/guides/source/caching_with_rails.textile index 3ee36ae971..712440da32 100644 --- a/guides/source/caching_with_rails.textile +++ b/guides/source/caching_with_rails.textile @@ -33,7 +33,7 @@ class ProductsController < ActionController caches_page :index def index - @products = Products.all + @products = Product.all end end </ruby> @@ -52,7 +52,7 @@ class ProductsController < ActionController caches_page :index def index - @products = Products.all + @products = Product.all end def create @@ -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 </ruby> +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+: + +<ruby> +class ProductsController < ApplicationController + def show + @product = Product.find(params[:id]) + respond_with(@product) if stale?(@product) + end +end +</ruby> + 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: <ruby> diff --git a/guides/source/command_line.textile b/guides/source/command_line.textile index 19e42cea93..39b75c2781 100644 --- a/guides/source/command_line.textile +++ b/guides/source/command_line.textile @@ -160,7 +160,7 @@ $ rails generate controller Greetings hello create app/assets/stylesheets/greetings.css.scss </shell> -What all did this generate? It made sure a bunch of directories were in our application, and created a controller file, a view file, a functional test file, a helper for the view, a javascript file and a stylesheet file. +What all did this generate? It made sure a bunch of directories were in our application, and created a controller file, a view file, a functional test file, a helper for the view, a JavaScript file and a stylesheet file. Check out the controller and modify it a little (in +app/controllers/greetings_controller.rb+): @@ -477,6 +477,53 @@ h4. Miscellaneous * +rake secret+ will give you a pseudo-random key to use for your session secret. * <tt>rake time:zones:all</tt> lists all the timezones Rails knows about. +h4. Writing Rake Tasks + +If you have (or want to write) any automation scripts outside your app (data import, checks, etc), you can make them as rake tasks. It's easy. + +INFO: "Complete guide about how to write tasks":http://rake.rubyforge.org/files/doc/rakefile_rdoc.html is available in the official documentation. + +Tasks should be placed in <tt>Rails.root/lib/tasks</tt> and should have a +.rake+ extension. + +Each task should be defined in next format (dependencies are optional): + +<ruby> +desc "I am short, but comprehensive description for my cool task" +task :task_name => [:prerequisite_task, :another_task_we_depend_on] do + # All your magick here + # Any valid Ruby code is allowed +end +</ruby> + +If you need to pass parameters, you can use next format (both arguments and dependencies are optional): + +<ruby> +task :task_name, [:arg_1] => [:pre_1, :pre_2] do |t, args| + # You can use args from here +end +</ruby> + +You can group tasks by placing them in namespaces: + +<ruby> +namespace :do + desc "This task does nothing" + task :nothing do + # Seriously, nothing + end +end +</ruby> + +You can see your tasks to be listed by <tt>rake -T</tt> command. And, according to the examples above, you can invoke them as follows: + +<shell> +rake task_name +rake "task_name[value 1]" # entire argument string should be quoted +rake do:nothing +</shell> + +NOTE: If your need to interact with your application models, perform database queries and so on, your task should depend on the +environment+ task, which will load your application code. + h3. The Rails Advanced Command Line More advanced use of the command line is focused around finding useful (even surprising at times) options in the utilities, and fitting those to your needs and specific work flow. Listed here are some tricks up Rails' sleeve. diff --git a/guides/source/configuring.textile b/guides/source/configuring.textile index cd9aab4892..dd84754b22 100644 --- a/guides/source/configuring.textile +++ b/guides/source/configuring.textile @@ -50,8 +50,6 @@ config.after_initialize do end </ruby> -* +config.allow_concurrency+ should be true to allow concurrent (threadsafe) action processing. False by default. You probably don't want to call this one directly, though, because a series of other adjustments need to be made for threadsafe mode to work properly. Can also be enabled with +threadsafe!+. - * +config.asset_host+ sets the host for the assets. Useful when CDNs are used for hosting assets, or when you want to work around the concurrency constraints builtin in browsers using different domain aliases. Shorter version of +config.action_controller.asset_host+. * +config.asset_path+ lets you decorate asset paths. This can be a callable, a string, or be +nil+ which is the default. For example, the normal path for +blog.js+ would be +/javascripts/blog.js+, let that absolute path be +path+. If +config.asset_path+ is a callable, Rails calls it when generating asset paths passing +path+ as argument. If +config.asset_path+ is a string, it is expected to be a +sprintf+ format string with a +%s+ where +path+ will get inserted. In either case, Rails outputs the decorated path. Shorter version of +config.action_controller.asset_path+. @@ -89,6 +87,10 @@ end * +config.dependency_loading+ is a flag that allows you to disable constant autoloading setting it to false. It only has effect if +config.cache_classes+ is true, which it is by default in production mode. This flag is set to false by +config.threadsafe!+. +* +config.eager_load+ when true, eager loads all registered `config.eager_load_namespaces`. This includes your application, engines, Rails frameworks and any other registered namespace. + +* +config.eager_load_namespaces+ registers namespaces that are eager loaded when +config.eager_load+ is true. All namespaces in the list must respond to the +eager_load!+ method. + * +config.eager_load_paths+ accepts an array of paths from which Rails will eager load on boot if cache classes is enabled. Defaults to every folder in the +app+ directory of the application. * +config.encoding+ sets up the application-wide encoding. Defaults to UTF-8. @@ -109,8 +111,6 @@ end * +config.middleware+ allows you to configure the application's middleware. This is covered in depth in the "Configuring Middleware":#configuring-middleware section below. -* +config.preload_frameworks+ enables or disables preloading all frameworks at startup. Enabled by +config.threadsafe!+. Defaults to +nil+, so is disabled. - * +config.queue+ configures a different queue implementation for the application. Defaults to +Rails::Queueing::Queue+. Note that, if the default queue is changed, the default +queue_consumer+ is not going to be initialized, it is up to the new queue implementation to handle starting and shutting down its own consumer(s). * +config.queue_consumer+ configures a different consumer implementation for the default queue. Defaults to +Rails::Queueing::ThreadedConsumer+. @@ -127,11 +127,7 @@ end config.session_store :my_custom_store </ruby> -This custom store must be defined as +ActionDispatch::Session::MyCustomStore+. In addition to symbols, they can also be objects implementing a certain API, like +ActiveRecord::SessionStore+, in which case no special namespace is required. - -* +config.threadsafe!+ enables +allow_concurrency+, +cache_classes+, +dependency_loading+ and +preload_frameworks+ to make the application threadsafe. - -WARNING: Threadsafe operation is incompatible with the normal workings of development mode Rails. In particular, automatic dependency loading and class reloading are automatically disabled when you call +config.threadsafe!+. +This custom store must be defined as +ActionDispatch::Session::MyCustomStore+. * +config.time_zone+ sets the default time zone for the application and enables time zone awareness for Active Record. @@ -157,7 +153,7 @@ Rails 3.1, by default, is set up to use the +sprockets+ gem to manage assets wit * +config.assets.digest+ enables the use of MD5 fingerprints in asset names. Set to +true+ by default in +production.rb+. -* +config.assets.debug+ disables the concatenation and compression of assets. Set to +false+ by default in +development.rb+. +* +config.assets.debug+ disables the concatenation and compression of assets. Set to +true+ by default in +development.rb+. * +config.assets.manifest+ defines the full path to be used for the asset precompiler's manifest file. Defaults to using +config.assets.prefix+. @@ -186,7 +182,7 @@ The full set of methods that can be used in this block are as follows: * +force_plural+ allows pluralized model names. Defaults to +false+. * +helper+ defines whether or not to generate helpers. Defaults to +true+. * +integration_tool+ defines which integration tool to use. Defaults to +nil+. -* +javascripts+ turns on the hook for javascripts in generators. Used in Rails for when the +scaffold+ generator is run. Defaults to +true+. +* +javascripts+ turns on the hook for JavaScript files in generators. Used in Rails for when the +scaffold+ generator is run. Defaults to +true+. * +javascript_engine+ configures the engine to be used (for eg. coffee) when generating assets. Defaults to +nil+. * +orm+ defines which orm to use. Defaults to +false+ and will use Active Record by default. * +performance_tool+ defines which performance tool to use. Defaults to +nil+. @@ -203,7 +199,7 @@ Every Rails application comes with a standard set of middleware which it uses in * +ActionDispatch::SSL+ forces every request to be under HTTPS protocol. Will be available if +config.force_ssl+ is set to +true+. Options passed to this can be configured by using +config.ssl_options+. * +ActionDispatch::Static+ is used to serve static assets. Disabled if +config.serve_static_assets+ is +true+. -* +Rack::Lock+ wraps the app in mutex so it can only be called by a single thread at a time. Only enabled if +config.action_controller.allow_concurrency+ is set to +false+, which it is by default. +* +Rack::Lock+ wraps the app in mutex so it can only be called by a single thread at a time. Only enabled when +config.cache_classes_+ is +false+. * +ActiveSupport::Cache::Strategy::LocalCache+ serves as a basic memory backed cache. This cache is not thread safe and is intended only for serving as a temporary memory cache for a single thread. * +Rack::Runtime+ sets an +X-Runtime+ header, containing the time (in seconds) taken to execute the request. * +Rails::Rack::Logger+ notifies the logs that the request has began. After request is complete, flushes all the logs. @@ -286,8 +282,6 @@ h4. Configuring Active Record * +config.active_record.auto_explain_threshold_in_seconds+ configures the threshold for automatic EXPLAINs (+nil+ disables this feature). Queries exceeding the threshold get their query plan logged. Default is 0.5 in development mode. -* +config.active_record.dependent_restrict_raises+ will control the behavior when an object with a <tt>:dependent => :restrict</tt> association is deleted. Setting this to false will prevent +DeleteRestrictionError+ from being raised and instead will add an error on the model object. Defaults to false in the development mode. - * +config.active_record.mass_assignment_sanitizer+ will determine the strictness of the mass assignment sanitization within Rails. Defaults to +:strict+. In this mode, mass assigning any non-+attr_accessible+ attribute in a +create+ or +update_attributes+ call will raise an exception. Setting this option to +:logger+ will only print to the log file when an attribute is being assigned and will not raise an exception. The MySQL adapter adds one additional configuration option: @@ -328,18 +322,16 @@ The caching code adds two additional settings: * +ActionController::Base.page_cache_extension+ sets the extension to be used when generating pages for the cache (this is ignored if the incoming request already has an extension). The default is +.html+. -The Active Record session store can also be configured: - -* +ActiveRecord::SessionStore::Session.table_name+ sets the name of the table used to store sessions. Defaults to +sessions+. - -* +ActiveRecord::SessionStore::Session.primary_key+ sets the name of the ID column used in the sessions table. Defaults to +session_id+. - -* +ActiveRecord::SessionStore::Session.data_column_name+ sets the name of the column which stores marshaled session data. Defaults to +data+. - h4. Configuring Action Dispatch * +config.action_dispatch.session_store+ sets the name of the store for session data. The default is +:cookie_store+; other valid options include +:active_record_store+, +:mem_cache_store+ or the name of your own custom class. +* +config.action_dispatch.default_headers+ is a hash with HTTP headers that are set by default in each response. By default, this is defined as: + +<ruby> +config.action_dispatch.default_headers = { 'X-Frame-Options' => 'SAMEORIGIN', 'X-XSS-Protection' => '1; mode=block', 'X-Content-Type-Options' => 'nosniff' } +</ruby> + * +config.action_dispatch.tld_length+ sets the TLD (top-level domain) length for the application. Defaults to +1+. * +ActionDispatch::Callbacks.before+ takes a block of code to run before the request. @@ -396,7 +388,7 @@ And can reference in the view with the following code: <erb> <%= render @post %> -<erb> +</erb> 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+. @@ -652,8 +644,6 @@ Serves as a placeholder so that +:load_environment_config+ can be defined to run *+load_active_support+* Requires +active_support/dependencies+ which sets up the basis for Active Support. Optionally requires +active_support/all+ if +config.active_support.bare+ is un-truthful, which is the default. -*+preload_frameworks+* Loads all autoload dependencies of Rails automatically if +config.preload_frameworks+ is +true+ or "truthful". By default this configuration option is disabled. In Rails, when internal classes are referenced for the first time they are autoloaded. +:preload_frameworks+ loads all of this at once on initialization. - *+initialize_logger+* Initializes the logger (an +ActiveSupport::BufferedLogger+ object) for the application and makes it accessible at +Rails.logger+, provided that no initializer inserted before this point has defined +Rails.logger+. *+initialize_cache+* If +Rails.cache+ isn't set yet, initializes the cache by referencing the value in +config.cache_store+ and stores the outcome as +Rails.cache+. If this object responds to the +middleware+ method, its middleware is inserted before +Rack::Runtime+ in the middleware stack. @@ -748,13 +738,13 @@ The error occurred while evaluating nil.each *+build_middleware_stack+* Builds the middleware stack for the application, returning an object which has a +call+ method which takes a Rack environment object for the request. -*+eager_load!+* If +config.cache_classes+ is true, runs the +config.before_eager_load+ hooks and then calls +eager_load!+ which will load all the Ruby files from +config.eager_load_paths+. +*+eager_load!+* If +config.eager_load+ is true, runs the +config.before_eager_load+ hooks and then calls +eager_load!+ which will load all +config.eager_load_namespaces+. *+finisher_hook+* Provides a hook for after the initialization of process of the application is complete, as well as running all the +config.after_initialize+ blocks for the application, railties and engines. *+set_routes_reloader+* Configures Action Dispatch to reload the routes file using +ActionDispatch::Callbacks.to_prepare+. -*+disable_dependency_loading+* Disables the automatic dependency loading if the +config.cache_classes+ is set to true and +config.dependency_loading+ is set to false. +*+disable_dependency_loading+* Disables the automatic dependency loading if the +config.eager_load+ is set to true. h3. Database pooling diff --git a/guides/source/contributing_to_ruby_on_rails.textile b/guides/source/contributing_to_ruby_on_rails.textile index 1dadce2083..427733fe03 100644 --- a/guides/source/contributing_to_ruby_on_rails.textile +++ b/guides/source/contributing_to_ruby_on_rails.textile @@ -34,20 +34,28 @@ h4. What about Feature Requests? Please don't put "feature request" items into GitHub Issues. If there's a new feature that you want to see added to Ruby on Rails, you'll need to write the code yourself - or convince someone else to partner with you to write the code. Later in this guide you'll find detailed instructions for proposing a patch to Ruby on Rails. If you enter a wishlist item in GitHub Issues with no code, you can expect it to be marked "invalid" as soon as it's reviewed. -h3. Running the Test Suite +If you'd like feedback on an idea for a feature before doing the work for make a patch, please send an email to the "rails-core mailing list":https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core. You might get no response, which means that everyone is indifferent. You might find someone who's also interested in building that feature. You might get a "This won't be accepted." But it's the proper place to discuss new ideas. GitHub Issues are not a particularly good venue for the sometimes long and involved discussions new features require. + +h3. Setting Up a Development Environment To move on from submitting bugs to helping resolve existing issues or contributing your own code to Ruby on Rails, you _must_ be able to run its test suite. In this section of the guide you'll learn how to set up the tests on your own computer. -h4. Install Git +h4. The Easy Way + +The easiest way to get a development environment ready to hack is to use the "Rails development box":https://github.com/rails/rails-dev-box. + +h4. The Hard Way + +h5. Install Git -Ruby on Rails uses git for source code control. The "git homepage":http://git-scm.com/ has installation instructions. There are a variety of resources on the net that will help you get familiar with git: +Ruby on Rails uses Git for source code control. The "Git homepage":http://git-scm.com/ has installation instructions. There are a variety of resources on the net that will help you get familiar with Git: -* "Everyday Git":http://schacon.github.com/git/everyday.html will teach you just enough about git to get by. -* The "PeepCode screencast":https://peepcode.com/products/git on git ($9) is easier to follow. -* "GitHub":http://help.github.com offers links to a variety of git resources. -* "Pro Git":http://progit.org/book/ is an entire book about git with a Creative Commons license. +* "Everyday Git":http://schacon.github.com/git/everyday.html will teach you just enough about Git to get by. +* The "PeepCode screencast":https://peepcode.com/products/git on Git ($9) is easier to follow. +* "GitHub":http://help.github.com offers links to a variety of Git resources. +* "Pro Git":http://git-scm.com/book is an entire book about Git with a Creative Commons license. -h4. Clone the Ruby on Rails Repository +h5. Clone the Ruby on Rails Repository Navigate to the folder where you want the Ruby on Rails source code (it will create its own +rails+ subdirectory) and run: @@ -56,7 +64,7 @@ $ git clone git://github.com/rails/rails.git $ cd rails </shell> -h4. Set up and Run the Tests +h5. Set up and Run the Tests The test suite must pass with any submitted code. No matter whether you are writing a new patch, or evaluating someone else's, you need to be able to run the tests. @@ -66,12 +74,26 @@ Install first libxml2 and libxslt together with their development files for Noko $ sudo apt-get install libxml2 libxml2-dev libxslt1-dev </shell> +If you are on Fedora or CentOS, you can run + +<shell> +$ sudo yum install libxml2 libxml2-devel libxslt libxslt-devel +</shell> + +If you have any problems with these libraries, you should install them manually compiling the source code. Just follow the instructions at the "Red Hat/CentOS section of the Nokogiri tutorials":http://nokogiri.org/tutorials/installing_nokogiri.html#red_hat__centos . + Also, SQLite3 and its development files for the +sqlite3-ruby+ gem -- in Ubuntu you're done with just <shell> $ sudo apt-get install sqlite3 libsqlite3-dev </shell> +And if you are on Fedora or CentOS, you're done with + +<shell> +$ sudo yum install sqlite3 sqlite3-devel +</shell> + Get a recent version of "Bundler":http://gembundler.com/: <shell> @@ -112,42 +134,30 @@ $ cd actionpack $ bundle exec ruby -Itest test/template/form_helper_test.rb </shell> -h4. Warnings - -The test suite runs with warnings enabled. Ideally, Ruby on Rails should issue no warnings, but there may be a few, as well as some from third-party libraries. Please ignore (or fix!) them, if any, and submit patches that do not issue new warnings. - -As of this writing (December, 2010) they are specially noisy with Ruby 1.9. If you are sure about what you are doing and would like to have a more clear output, there's a way to override the flag: - -<shell> -$ RUBYOPT=-W0 bundle exec rake test -</shell> - -h4. Testing Active Record +h5. Active Record Setup The test suite of Active Record attempts to run four times: once for SQLite3, once for each of the two MySQL gems (+mysql+ and +mysql2+), and once for PostgreSQL. We are going to see now how to set up the environment for them. WARNING: If you're working with Active Record code, you _must_ ensure that the tests pass for at least MySQL, PostgreSQL, and SQLite3. Subtle differences between the various adapters have been behind the rejection of many patches that looked OK when tested only against MySQL. -h5. Database Configuration +h6. Database Configuration The Active Record test suite requires a custom config file: +activerecord/test/config.yml+. An example is provided in +activerecord/test/config.example.yml+ which can be copied and used as needed for your environment. -h5. SQLite3 +h6. MySQL and PostgreSQL -The gem +sqlite3-ruby+ does not belong to the "db" group. Indeed, if you followed the instructions above you're ready. This is how you run the Active Record test suite only for SQLite3: +To be able to run the suite for MySQL and PostgreSQL we need their gems. Install first the servers, their client libraries, and their development files. In Ubuntu just run <shell> -$ cd activerecord -$ bundle exec rake test_sqlite3 +$ sudo apt-get install mysql-server libmysqlclient15-dev +$ sudo apt-get install postgresql postgresql-client postgresql-contrib libpq-dev </shell> -h5. MySQL and PostgreSQL - -To be able to run the suite for MySQL and PostgreSQL we need their gems. Install first the servers, their client libraries, and their development files. In Ubuntu just run +On Fedora or CentOS, just run: <shell> -$ sudo apt-get install mysql-server libmysqlclient15-dev -$ sudo apt-get install postgresql postgresql-client postgresql-contrib libpq-dev +$ sudo yum install mysql-server mysql-devel +$ sudo yum install postgresql-server postgresql-devel </shell> After that run: @@ -172,7 +182,7 @@ and create the test databases: <shell> $ cd activerecord -$ rake mysql:build_databases +$ bundle exec rake mysql:build_databases </shell> PostgreSQL's authentication works differently. A simple way to set up the development environment for example is to run with your development account @@ -185,14 +195,23 @@ and then create the test databases with <shell> $ cd activerecord -$ rake postgresql:build_databases +$ bundle exec rake postgresql:build_databases </shell> NOTE: Using the rake task to create the test databases ensures they have the correct character set and collation. NOTE: You'll see the following warning (or localized warning) during activating HStore extension in PostgreSQL 9.1.x or earlier: "WARNING: => is deprecated as an operator". -If you’re using another database, check the files under +activerecord/test/connections+ for default connection information. You can edit these files to provide different credentials on your machine if you must, but obviously you should not push any such changes back to Rails. +If you’re using another database, check the file +activerecord/test/config.yml+ or +activerecord/test/config.example.yml+ for default connection information. You can edit +activerecord/test/config.yml+ to provide different credentials on your machine if you must, but obviously you should not push any such changes back to Rails. + +h3. Testing Active Record + +This is how you run the Active Record test suite only for SQLite3: + +<shell> +$ cd activerecord +$ bundle exec rake test_sqlite3 +</shell> You can now run the tests as you did for +sqlite3+. The tasks are respectively @@ -202,7 +221,7 @@ test_mysql2 test_postgresql </shell> -As we mentioned before +Finally, <shell> $ bundle exec rake test @@ -218,6 +237,16 @@ $ ARCONN=sqlite3 ruby -Itest test/cases/associations/has_many_associations_test. You can invoke +test_jdbcmysql+, +test_jdbcsqlite3+ or +test_jdbcpostgresql+ also. See the file +activerecord/RUNNING_UNIT_TESTS+ for information on running more targeted database tests, or the file +ci/travis.rb+ for the test suite run by the continuous integration server. +h4. Warnings + +The test suite runs with warnings enabled. Ideally, Ruby on Rails should issue no warnings, but there may be a few, as well as some from third-party libraries. Please ignore (or fix!) them, if any, and submit patches that do not issue new warnings. + +As of this writing (December, 2010) they are specially noisy with Ruby 1.9. If you are sure about what you are doing and would like to have a more clear output, there's a way to override the flag: + +<shell> +$ RUBYOPT=-W0 bundle exec rake test +</shell> + h4. Older Versions of Ruby on Rails If you want to add a fix to older versions of Ruby on Rails, you'll need to set up and switch to your own local tracking branch. Here is an example to switch to the 3-0-stable branch: @@ -227,7 +256,7 @@ $ git branch --track 3-0-stable origin/3-0-stable $ git checkout 3-0-stable </shell> -TIP: You may want to "put your git branch name in your shell prompt":http://qugstart.com/blog/git-and-svn/add-colored-git-branch-name-to-your-shell-prompt/ to make it easier to remember which version of the code you're working with. +TIP: You may want to "put your Git branch name in your shell prompt":http://qugstart.com/blog/git-and-svn/add-colored-git-branch-name-to-your-shell-prompt/ to make it easier to remember which version of the code you're working with. h3. Helping to Resolve Existing Issues @@ -308,7 +337,7 @@ $ cd rails $ git checkout -b my_new_branch </shell> -It doesn’t matter much what name you use, because this branch will only exist on your local computer and your personal repository on Github. It won't be part of the Rails git repository. +It doesn’t matter much what name you use, because this branch will only exist on your local computer and your personal repository on Github. It won't be part of the Rails Git repository. h4. Write Your Code @@ -336,6 +365,31 @@ Rails follows a simple set of coding style conventions. The above are guidelines -- please use your best judgment in using them. +h4. Updating the CHANGELOG + +The CHANGELOG is an important part of every release. It keeps the list of changes for every Rails version. + +You should add an entry to the CHANGELOG of the framework that you modified if you're adding or removing a feature, commiting a bug fix or adding deprecation notices. Refactorings and documentation changes generally should not go to the CHANGELOG. + +A CHANGELOG entry should summarize what was changed and should end with author's name. You can use multiple lines if you need more space and you can attach code examples indented with 4 spaces. If a change is related to a specific issue, you should attach issue's number. Here is an example CHANGELOG entry: + +<plain> +* Summary of a change that briefly describes what was changed. You can use multiple + lines and wrap them at around 80 characters. Code examples are ok, too, if needed: + + class Foo + def bar + puts 'baz' + end + end + + You can continue after the code example and you can attach issue number. GH#1234 + + *Your Name* +</plain> + +Your name can be added directly after the last word if you don't provide any code examples or don't need multiple paragraphs. Otherwise, it's best to make as a new paragraph. + h4. Sanity Check You should not be the only person who looks at the code before you submit it. You know at least one other Rails developer, right? Show them what you’re doing and ask for feedback. Doing this in private before you push a patch out publicly is the “smoke test” for a patch: if you can’t convince one other developer of the beauty of your code, you’re unlikely to convince the core team either. @@ -344,7 +398,7 @@ You might want also to check out the "RailsBridge BugMash":http://wiki.railsbrid h4. Commit Your Changes -When you're happy with the code on your computer, you need to commit the changes to git: +When you're happy with the code on your computer, you need to commit the changes to Git: <shell> $ git commit -a @@ -363,7 +417,7 @@ the commit content is obvious, it may not be obvious to others. You should add such description also if it's already present in bug tracker, it should not be necessary to visit a webpage to check the history. -Description can have multiple paragraps and you can use code examples +Description can have multiple paragraphs and you can use code examples inside, just indent it with 4 spaces: class PostsController @@ -472,7 +526,7 @@ It’s entirely possible that the feedback you get will suggest changes. Don’t h4. Backporting -Changes that are merged into master are intended for the next major release of Rails. Sometimes, it might be beneficial for your changes to propagate back to the maintenance releases for older stable branches. Generally, security fixes and bug fixes are good candidates for a backport, while new features and patches that introduce a change in behavior will not be accepted. When in doubt, it is best to consult a rails team member before backporting your changes to avoid wasted effort. +Changes that are merged into master are intended for the next major release of Rails. Sometimes, it might be beneficial for your changes to propagate back to the maintenance releases for older stable branches. Generally, security fixes and bug fixes are good candidates for a backport, while new features and patches that introduce a change in behavior will not be accepted. When in doubt, it is best to consult a Rails team member before backporting your changes to avoid wasted effort. For simple fixes, the easiest way to backport your changes is to "extract a diff from your changes in master and apply them to the target branch":http://ariejan.net/2009/10/26/how-to-create-and-apply-a-patch-with-git. @@ -497,9 +551,9 @@ $ git apply ~/my_changes.patch This works well for simple changes. However, if your changes are complicated or if the code in master has deviated significantly from your target branch, it might require more work on your part. The difficulty of a backport varies greatly from case to case, and sometimes it is simply not worth the effort. -Once you have resolved all conflicts and made sure all the tests are passing, push your changes and open a separate pull request for your backport. It is also worth noting that older branches might have a different set of build targets than master. When possible, it is best to first test your backport locally against the ruby versions listed in +.travis.yml+ before submitting your pull request. +Once you have resolved all conflicts and made sure all the tests are passing, push your changes and open a separate pull request for your backport. It is also worth noting that older branches might have a different set of build targets than master. When possible, it is best to first test your backport locally against the Ruby versions listed in +.travis.yml+ before submitting your pull request. -And then ... think about your next contribution! +And then... think about your next contribution! h3. Rails Contributors diff --git a/guides/source/credits.html.erb b/guides/source/credits.html.erb index 04deec6a11..e25168d58d 100644 --- a/guides/source/credits.html.erb +++ b/guides/source/credits.html.erb @@ -64,7 +64,7 @@ Oscar Del Ben is a software engineer at <a href="http://www.wildfireapp.com/">Wi <% end %> <%= author('Pratik Naik', 'lifo') do %> - Pratik Naik is a Ruby on Rails consultant with <a href="http://www.actionrails.com">ActionRails</a> and also a member of the <a href="http://rubyonrails.org/core">Rails core team</a>. He maintains a blog at <a href="http://m.onkey.org">has_many :bugs, :through => :rails</a> and has an active <a href="http://twitter.com/lifo">twitter account</a>. + Pratik Naik is a Ruby on Rails developer at <a href="http://www.37signals.com">37signals</a> and also a member of the <a href="http://rubyonrails.org/core">Rails core team</a>. He maintains a blog at <a href="http://m.onkey.org">has_many :bugs, :through => :rails</a> and has a semi-active <a href="http://twitter.com/lifo">twitter account</a>. <% end %> <%= author('Emilio Tagua', 'miloops') do %> diff --git a/guides/source/debugging_rails_applications.textile b/guides/source/debugging_rails_applications.textile index cc172042e9..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. + +<ruby> +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" +</ruby> + 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. @@ -626,7 +637,7 @@ In this section, you will learn how to find and fix such leaks by using tools su h4. BleakHouse -"BleakHouse":https://github.com/fauna/bleak_house/tree/master is a library for finding memory leaks. +"BleakHouse":https://github.com/evan/bleak_house/ is a library for finding memory leaks. If a Ruby object does not go out of scope, the Ruby Garbage Collector won't sweep it since it is referenced somewhere. Leaks like this can grow slowly and your application will consume more and more memory, gradually affecting the overall system performance. This tool will help you find leaks on the Ruby heap. @@ -675,7 +686,7 @@ To analyze it, just run the listed command. The top 20 leakiest lines will be li This way you can find where your application is leaking memory and fix it. -If "BleakHouse":https://github.com/fauna/bleak_house/tree/master doesn't report any heap growth but you still have memory growth, you might have a broken C extension, or real leak in the interpreter. In that case, try using Valgrind to investigate further. +If "BleakHouse":https://github.com/evan/bleak_house/ doesn't report any heap growth but you still have memory growth, you might have a broken C extension, or real leak in the interpreter. In that case, try using Valgrind to investigate further. h4. Valgrind @@ -708,4 +719,4 @@ h3. References * "Debugging with ruby-debug":http://bashdb.sourceforge.net/ruby-debug.html * "ruby-debug cheat sheet":http://cheat.errtheblog.com/s/rdebug/ * "Ruby on Rails Wiki: How to Configure Logging":http://wiki.rubyonrails.org/rails/pages/HowtoConfigureLogging -* "Bleak House Documentation":http://blog.evanweaver.com/files/doc/fauna/bleak_house/files/README.html +* "Bleak House Documentation":http://blog.evanweaver.com/files/doc/fauna/bleak_house/ diff --git a/guides/source/engines.textile b/guides/source/engines.textile index 86e7254201..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 </shell> -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+: <ruby> 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+: <ruby> gem 'blorgh', :path => "vendor/engines/blorgh" @@ -69,7 +69,7 @@ module Blorgh end </ruby> -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 </shell> -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 </ruby> -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: @@ -217,7 +217,7 @@ This helps prevent conflicts with any other engine or application that may have Finally, two files that are the assets for this resource are generated, +app/assets/javascripts/blorgh/posts.js+ and +app/assets/javascripts/blorgh/posts.css+. You'll see how to use these a little later. -By default, the scaffold styling is not applied to the engine as the engine's layout file, +app/views/blorgh/application.html.erb+ doesn't load it. To make this apply, insert this line into the +<head>+ tag of this layout: +By default, the scaffold styling is not applied to the engine as the engine's layout file, +app/views/blorgh/application.html.erb+ doesn't load it. To make this apply, insert this line into the +<head>+ tag of this layout: <erb> <%= stylesheet_link_tag "scaffold" %> @@ -347,7 +347,7 @@ The form will be making a +POST+ request to +/posts/:post_id/comments+, which wi <ruby> def create @post = Post.find(params[:post_id]) - @comment = @post.comments.build(params[:comment]) + @comment = @post.comments.create(params[:comment]) flash[:notice] = "Comment has been created!" redirect_to post_path end @@ -361,7 +361,7 @@ Missing partial blorgh/comments/comment with {:handlers=>[:erb, :builder], :form * "/Users/ryan/Sites/side_projects/blorgh/app/views" </plain> -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: @@ -369,13 +369,13 @@ This partial will be responsible for rendering just the comment text, for now. C <%= comment_counter + 1 %>. <%= comment.text %> </erb> -The +comment_counter+ local variable is given to us by the +<%= render @post.comments %>+ call, as it will define this automatically and increment the counter as it iterates through each comment. It's used in this example to display a small number next to each comment when it's created. +The +comment_counter+ local variable is given to us by the +<%= render @post.comments %>+ call, as it will define this automatically and increment the counter as it iterates through each comment. It's used in this example to display a small number next to each comment when it's created. That completes the comment function of the blogging engine. Now it's time to use it within an application. 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' </ruby> -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+: <ruby> gem 'blorgh', :path => "/path/to/blorgh" </ruby> -If the whole +blorgh+ engine directory is copied to +vendor/engines/blorgh+ then it could be specified in the +Gemfile+ like this: - -<ruby> -gem 'blorgh', :path => "vendor/engines/blorgh" -</ruby> - 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 </p> </erb> -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: <plain> #<User:0x00000100ccb3b0> @@ -563,11 +557,11 @@ end By default, the engine's controllers inherit from <tt>Blorgh::ApplicationController</tt>. So, after making this change they will have access to the main applications +ApplicationController+ as though they were part of the main application. -This change does require that the engine is run from a Rails application that has an +ApplicationController+. +This change does require that the engine is run from a Rails application that has an +ApplicationController+. h4. Configuring an engine -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 </ruby> -This would then turn the above code for +self.author=+ into this: +This would then turn the above code for +set_author+ into this: <ruby> 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 @@ -655,7 +649,125 @@ This tells the application that you still want to perform a +GET+ request to the h3. Improving engine functionality -This section looks at overriding or adding functionality to the views, controllers and models provided by an engine. +This section explains how to add and/or override engine MVC functionality in the main Rails application. + +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+. + +h5. Implementing Decorator Pattern Using Class#class_eval + +**Adding** +Post#time_since_created+, + +<ruby> +# MyApp/app/decorators/models/blorgh/post_decorator.rb + +Blorgh::Post.class_eval do + def time_since_created + Time.current - created_at + end +end +</ruby> + +<ruby> +# Blorgh/app/models/post.rb + +class Post < ActiveRecord::Base + has_many :comments +end +</ruby> + + +**Overriding** +Post#summary+ + +<ruby> +# MyApp/app/decorators/models/blorgh/post_decorator.rb + +Blorgh::Post.class_eval do + def summary + "#{title} - #{truncate(text)}" + end +end +</ruby> + +<ruby> +# Blorgh/app/models/post.rb + +class Post < ActiveRecord::Base + has_many :comments + def summary + "#{title}" + end +end +</ruby> + + +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 the load order of interlinked dependencies at run time allowing you to significantly modularize your code. + +**Adding** +Post#time_since_created+<br/> +**Overriding** +Post#summary+ + +<ruby> +# MyApp/app/models/blorgh/post.rb + +class Blorgh::Post < ActiveRecord::Base + include Blorgh::Concerns::Models::Post + + def time_since_created + Time.current - created_at + end + + def summary + "#{title} - #{truncate(text)}" + end +end +</ruby> + +<ruby> +# Blorgh/app/models/post.rb + +class Post < ActiveRecord::Base + include Blorgh::Concerns::Models::Post +end +</ruby> + +<ruby> +# Blorgh/lib/concerns/models/post + +module Blorgh::Concerns::Models::Post + extend ActiveSupport::Concern + + # 'included do' causes the included code to be evaluated in the + # conext where it is included (post.rb), rather than be + # executed in the module's context (blorgh/concerns/models/post). + included do + attr_accessor :author_name + belongs_to :author, :class_name => "User" + + before_save :set_author + + private + + def set_author + self.author = User.find_or_create_by_name(author_name) + end + end + + def summary + "#{title}" + end + + module ClassMethods + def some_class_method + 'some class method string' + end + end +end +</ruby> h4. Overriding views @@ -663,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: @@ -734,12 +846,14 @@ You can also specify these assets as dependencies of other assets using the Asse */ </plain> +INFO. Remember that in order to use languages like Sass or CoffeeScript, you should add the relevant library to your engine's +.gemspec+. + h4. Separate Assets & Precompiling -There are some situations where your engine's assets not required by the host application. For example, say that you've created +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+ @@ -753,18 +867,39 @@ For more information, read the "Asset Pipeline guide":http://guides.rubyonrails. h4. Other gem dependencies -Gem dependencies inside an engine should be specified inside the +.gemspec+ file that's at the root of the engine. The reason for this is because the engine may be installed as a gem. If dependencies were to be specified inside the +Gemfile+, these would not be recognised by a traditional gem install and so they would not be installed, causing the engine to malfunction. +Gem dependencies inside an engine should be specified inside the +.gemspec+ file at the root of the engine. The reason for this is because the engine may +be installed as a gem. If dependencies were to be specified inside the +Gemfile+, +these would not be recognised by a traditional gem install and so they would not +be installed, causing the engine to malfunction. -To specify a dependency that should be installed with the engine during a traditional +gem install+, specify it inside the +Gem::Specification+ block inside the +.gemspec+ file in the engine: +To specify a dependency that should be installed with the engine during a +traditional +gem install+, specify it inside the +Gem::Specification+ block +inside the +.gemspec+ file in the engine: <ruby> s.add_dependency "moo" </ruby> -To specify a dependency that should only be installed as a development dependency of the application, specify it like this: +To specify a dependency that should only be installed as a development +dependency of the application, specify it like this: <ruby> s.add_development_dependency "moo" </ruby> -Both kinds of dependencies will be installed when +bundle install+ is run inside the application. The development dependencies for the gem will only be used when the tests for the engine are running. +Both kinds of dependencies will be installed when +bundle install+ is run inside +the application. The development dependencies for the gem will only be used when +the tests for the engine are running. + +Note that if you want to immediately require dependencies when the engine is +required, you should require them before the engine's initialization. For example: + +<ruby> +require 'other_engine/engine' +require 'yet_another_engine/engine' + +module MyEngine + class Engine < ::Rails::Engine + end +end +</ruby>
\ No newline at end of file diff --git a/guides/source/form_helpers.textile b/guides/source/form_helpers.textile index 1851aceff8..d704b78f83 100644 --- a/guides/source/form_helpers.textile +++ b/guides/source/form_helpers.textile @@ -10,7 +10,7 @@ In this guide you will: * Understand the date and time helpers Rails provides * Learn what makes a file upload form different * Learn some cases of building forms to external resources -* Find out where to look for complex forms +* Find out how to build complex forms endprologue. @@ -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: <ruby> form_tag(:controller => "people", :action => "search", :method => "get", :class => "nifty_form") @@ -98,7 +98,7 @@ form_tag({:controller => "people", :action => "search"}, :method => "get", :clas h4. Helpers for Generating Form Elements -Rails provides a series of helpers for generating form elements such as checkboxes, text fields, and radio buttons. These basic helpers, with names ending in "_tag" (such as +text_field_tag+ and +check_box_tag+), generate just a single +<input>+ element. The first parameter to these is always the name of the input. When the form is submitted, the name will be passed along with the form data, and will make its way to the +params+ hash in the controller with the value entered by the user for that field. For example, if the form contains +<%= text_field_tag(:query) %>+, then you would be able to get the value of this field in the controller with +params[:query]+. +Rails provides a series of helpers for generating form elements such as checkboxes, text fields, and radio buttons. These basic helpers, with names ending in "_tag" (such as +text_field_tag+ and +check_box_tag+), generate just a single +<input>+ element. The first parameter to these is always the name of the input. When the form is submitted, the name will be passed along with the form data, and will make its way to the +params+ hash in the controller with the value entered by the user for that field. For example, if the form contains +<%= text_field_tag(:query) %>+, then you would be able to get the value of this field in the controller with +params[:query]+. When naming inputs, Rails uses certain conventions that make it possible to submit parameters with non-scalar values such as arrays or hashes, which will also be accessible in +params+. You can read more about them in "chapter 7 of this guide":#understanding-parameter-naming-conventions. For details on the precise usage of these helpers, please refer to the "API documentation":http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html. @@ -654,7 +654,7 @@ If +f+ is an instance of FormBuilder then this will render the +form+ partial, s h3. Understanding Parameter Naming Conventions As you've seen in the previous sections, values from forms can be at the top level of the +params+ hash or nested in another hash. For example in a standard +create+ -action for a Person model, +params[:model]+ would usually be a hash of all the attributes for the person to create. The +params+ hash can also contain arrays, arrays of hashes and so on. +action for a Person model, +params[:person]+ would usually be a hash of all the attributes for the person to create. The +params+ hash can also contain arrays, arrays of hashes and so on. Fundamentally HTML forms don't know about any sort of structured data, all they generate is name–value pairs, where pairs are just plain strings. The arrays and hashes you see in your application are the result of some parameter naming conventions that Rails uses. @@ -816,11 +816,130 @@ Or if you don't want to render an +authenticity_token+ field: h3. Building Complex Forms -Many apps grow beyond simple forms editing a single object. For example when creating a Person you might want to allow the user to (on the same form) create multiple address records (home, work, etc.). When later editing that person the user should be able to add, remove or amend addresses as necessary. While this guide has shown you all the pieces necessary to handle this, Rails does not yet have a standard end-to-end way of accomplishing this, but many have come up with viable approaches. These include: +Many apps grow beyond simple forms editing a single object. For example when creating a Person you might want to allow the user to (on the same form) create multiple address records (home, work, etc.). When later editing that person the user should be able to add, remove or amend addresses as necessary. -* As of Rails 2.3, Rails includes "Nested Attributes":./2_3_release_notes.html#nested-attributes and "Nested Object Forms":./2_3_release_notes.html#nested-object-forms -* Ryan Bates' series of Railscasts on "complex forms":http://railscasts.com/episodes/75 -* Handle Multiple Models in One Form from "Advanced Rails Recipes":http://media.pragprog.com/titles/fr_arr/multiple_models_one_form.pdf -* Eloy Duran's "complex-forms-examples":https://github.com/alloy/complex-form-examples/ application -* Lance Ivy's "nested_assignment":https://github.com/cainlevy/nested_assignment/tree/master plugin and "sample application":https://github.com/cainlevy/complex-form-examples/tree/cainlevy -* James Golick's "attribute_fu":https://github.com/jamesgolick/attribute_fu plugin +h4. Configuring the Model + +Active Record provides model level support via the +accepts_nested_attributes_for+ method: + +<ruby> +class Person < ActiveRecord::Base + has_many :addresses + accepts_nested_attributes_for :addresses + + attr_accessible :name, :addresses_attributes +end + +class Address < ActiveRecord::Base + belongs_to :person + attr_accessible :kind, :street +end +</ruby> + +This creates an +addresses_attributes=+ method on +Person+ that allows you to create, update and (optionally) destroy addresses. When using +attr_accessible+ or +attr_protected+ you must mark +addresses_attributes+ as accessible as well as the other attributes of +Person+ and +Address+ that should be mass assigned. + +h4. Building the Form + +The following form allows a user to create a +Person+ and its associated addresses. + +<erb> +<%= form_for @person do |f| %> + Addresses: + <ul> + <%= f.fields_for :addresses do |addresses_form| %> + <li> + <%= addresses_form.label :kind %> + <%= addresses_form.text_field :kind %> + + <%= addresses_form.label :street %> + <%= addresses_form.text_field :street %> + ... + </li> + <% end %> + </ul> +<% end %> +</erb> + + +When an association accepts nested attributes +fields_for+ renders its block once for every element of the association. In particular, if a person has no addresses it renders nothing. A common pattern is for the controller to build one or more empty children so that at least one set of fields is shown to the user. The example below would result in 3 sets of address fields being rendered on the new person form. + +<ruby> +def new + @person = Person.new + 3.times { @person.addresses.build} +end +</ruby> + ++fields_for+ yields a form builder that names parameters in the format expected the accessor generated by +accepts_nested_attributes_for+. For example when creating a user with 2 addresses, the submitted parameters would look like + +<ruby> +{ + :person => { + :name => 'John Doe', + :addresses_attributes => { + '0' => { + :kind => 'Home', + :street => '221b Baker Street', + }, + '1' => { + :kind => 'Office', + :street => '31 Spooner Street' + } + } + } +} +</ruby> + +The keys of the +:addresses_attributes+ hash are unimportant, they need merely be different for each address. + +If the associated object is already saved, +fields_for+ autogenerates a hidden input with the +id+ of the saved record. You can disable this by passing +:include_id => false+ to +fields_for+. You may wish to do this if the autogenerated input is placed in a location where an input tag is not valid HTML or when using an ORM where children do not have an id. + +h4. The Controller + +You do not need to write any specific controller code to use nested attributes. Create and update records as you would with a simple form. + +h4. Removing Objects + +You can allow users to delete associated objects by passing +allow_destroy => true+ to +accepts_nested_attributes_for+ + +<ruby> +class Person < ActiveRecord::Base + has_many :addresses + accepts_nested_attributes_for :addresses, :allow_destroy => true +end +</ruby> + +If the hash of attributes for an object contains the key +_destroy+ with a value of '1' or 'true' then the object will be destroyed. This form allows users to remove addresses: + +<erb> +<%= form_for @person do |f| %> + Addresses: + <ul> + <%= f.fields_for :addresses do |addresses_form| %> + <li> + <%= check_box :_destroy%> + <%= addresses_form.label :kind %> + <%= addresses_form.text_field :kind %> + ... + </li> + <% end %> + </ul> +<% end %> +</erb> + +h4. Preventing Empty Records + +It is often useful to ignore sets of fields that the user has not filled in. You can control this by passing a +:reject_if+ proc to +accepts_nested_attributes_for+. This proc will be called with each hash of attributes submitted by the form. If the proc returns +false+ then Active Record will not build an associated object for that hash. The example below only tries to build an address if the +kind+ attribute is set. + +<ruby> +class Person < ActiveRecord::Base + has_many :addresses + accepts_nested_attributes_for :addresses, :reject_if => lambda {|attributes| attributes['kind'].blank?} +end +</ruby> + +As a convenience you can instead pass the symbol +:all_blank+ which will create a proc that will reject records where all the attributes are blank excluding any value for +_destroy+. + +h4. Adding Fields on the Fly + +Rather than rendering multiple sets of fields ahead of time you may wish to add them only when a user clicks on an 'Add new child' button. Rails does not provide any builtin support for this. When generating new sets of fields you must ensure the the key of the associated array is unique - the current javascript date (milliseconds after the epoch) is a common choice. diff --git a/guides/source/getting_started.textile b/guides/source/getting_started.textile index 07419d11b4..226c3dce14 100644 --- a/guides/source/getting_started.textile +++ b/guides/source/getting_started.textile @@ -124,7 +124,7 @@ application. Most of the work in this tutorial will happen in the +app/+ folder, |config.ru|Rack configuration for Rack based servers used to start the application.| |db/|Contains your current database schema, as well as the database migrations.| |doc/|In-depth documentation for your application.| -|Gemfile<BR />Gemfile.lock|These files allow you to specify what gem dependencies are needed for your Rails application. These files are used by the Bundler gem. For more information about Bundler, see "the Bundler website":http://gembundler.com | +|Gemfile<br />Gemfile.lock|These files allow you to specify what gem dependencies are needed for your Rails application. These files are used by the Bundler gem. For more information about Bundler, see "the Bundler website":http://gembundler.com | |lib/|Extended modules for your application.| |log/|Application log files.| |public/|The only folder seen to the world as-is. Contains the static files and compiled assets.| @@ -373,7 +373,7 @@ Edit the +form_for+ line inside +app/views/posts/new.html.erb+ to look like this In this example, a +Hash+ object is passed to the +:url+ option. What Rails will do with this is that it will point the form to the +create+ action of the current controller, the +PostsController+, and will send a +POST+ request to that route. For this to work, you will need to add a route to +config/routes.rb+, right underneath the one for "posts/new": <ruby> -post "posts/create" +post "posts" => "posts#create" </ruby> By using the +post+ method rather than the +get+ method, Rails will define a route that will only respond to POST methods. The POST method is the typical method used by forms all over the web. @@ -1064,13 +1064,13 @@ received an error before. <shell> # rake routes - posts GET /posts(.:format) posts#index - posts_new GET /posts/new(.:format) posts#new -posts_create POST /posts/create(.:format) posts#create - GET /posts/:id(.:format) posts#show - GET /posts/:id/edit(.:format) posts#edit - PUT /posts/:id(.:format) posts#update - root / welcome#index + posts GET /posts(.:format) posts#index +posts_new GET /posts/new(.:format) posts#new + POST /posts(.:format) posts#create + GET /posts/:id(.:format) posts#show + GET /posts/:id/edit(.:format) posts#edit + PUT /posts/:id(.:format) posts#update + root / welcome#index </shell> To fix this, open +config/routes.rb+ and modify the +get "posts/:id"+ @@ -1144,7 +1144,7 @@ together. <td><%= post.text %></td> <td><%= link_to 'Show', :action => :show, :id => post.id %></td> <td><%= link_to 'Edit', :action => :edit, :id => post.id %></td> - <td><%= link_to 'Destroy', { :action => :destroy, :id => post.id }, :method => :delete, :confirm => 'Are you sure?' %></td> + <td><%= link_to 'Destroy', { :action => :destroy, :id => post.id }, :method => :delete, :data => { :confirm => 'Are you sure?' } %></td> </tr> <% end %> </table> @@ -1152,13 +1152,12 @@ together. Here we're using +link_to+ in a different way. We wrap the +:action+ and +:id+ attributes in a hash so that we can pass those two keys in -first as one argument, and then the final two keys as another argument. The +:method+ and +:confirm+ +first as one argument, and then the final two keys as another argument. The +:method+ and +:'data-confirm'+ options are used as HTML5 attributes so that when the link is clicked, -Rails will first show a confirm dialog to the user, and then submit the -link with method +delete+. This is done via the JavaScript file +jquery_ujs+ -which is automatically included into your application's layout -(+app/views/layouts/application.html.erb+) when you generated the application. -Without this file, the confirmation dialog box wouldn't appear. +Rails will first show a confirm dialog to the user, and then submit the link with method +delete+. +This is done via the JavaScript file +jquery_ujs+ which is automatically included +into your application's layout (+app/views/layouts/application.html.erb+) when you +generated the application. Without this file, the confirmation dialog box wouldn't appear. !images/getting_started/confirm_dialog.png(Confirm Dialog)! @@ -1176,7 +1175,7 @@ declaring separate routes with the appropriate verbs into <ruby> get "posts" => "posts#index" get "posts/new" -post "posts/create" +post "posts" => "posts#create" get "posts/:id" => "posts#show", :as => :post get "posts/:id/edit" => "posts#edit" put "posts/:id" => "posts#update" @@ -1248,6 +1247,7 @@ First, take a look at +comment.rb+: <ruby> class Comment < ActiveRecord::Base belongs_to :post + attr_accessible :body, :commenter end </ruby> @@ -1626,8 +1626,8 @@ So first, let's add the delete link in the <p> <%= link_to 'Destroy Comment', [comment.post, comment], - :confirm => 'Are you sure?', - :method => :delete %> + :method => :delete, + :data => { :confirm => 'Are you sure?' } %> </p> </erb> diff --git a/guides/source/i18n.textile b/guides/source/i18n.textile index ee7176a6c8..c073a146a8 100644 --- a/guides/source/i18n.textile +++ b/guides/source/i18n.textile @@ -220,7 +220,7 @@ Every helper method dependent on +url_for+ (e.g. helpers for named routes like + You may be satisfied with this. It does impact the readability of URLs, though, when the locale "hangs" at the end of every URL in your application. Moreover, from the architectural standpoint, locale is usually hierarchically above the other parts of the application domain: and URLs should reflect this. -You probably want URLs to look like this: +www.example.com/en/books+ (which loads the English locale) and +www.example.com/nl/books+ (which loads the Dutch locale). This is achievable with the "over-riding +default_url_options+" strategy from above: you just have to set up your routes with "+path_prefix+":http://api.rubyonrails.org/classes/ActionController/Resources.html#M000354 option in this way: +You probably want URLs to look like this: +www.example.com/en/books+ (which loads the English locale) and +www.example.com/nl/books+ (which loads the Dutch locale). This is achievable with the "over-riding +default_url_options+" strategy from above: you just have to set up your routes with "+scoping+":http://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html option in this way: <ruby> # config/routes.rb @@ -405,6 +405,10 @@ So that would give you: TIP: Right now you might need to add some more date/time formats in order to make the I18n backend work as expected (at least for the 'pirate' locale). Of course, there's a great chance that somebody already did all the work by *translating Rails' defaults for your locale*. See the "rails-i18n repository at Github":https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale for an archive of various locale files. When you put such file(s) in +config/locales/+ directory, they will automatically be ready for use. +h4. Inflection Rules For Other Locales + +Rails 4.0 allows you to define inflection rules (such as rules for singularization and pluralization) for locales other than English. In +config/initializers/inflections.rb+, you can define these rules for multiple locales. The initializer contains a default example for specifying additional rules for English; follow that format for other locales as you see fit. + h4. Localized Views Rails 2.3 introduces another convenient localization feature: localized views (templates). Let's say you have a _BooksController_ in your application. Your _index_ action renders content in +app/views/books/index.html.erb+ template. When you put a _localized variant_ of this template: *+index.es.html.erb+* in the same directory, Rails will render content in this template, when the locale is set to +:es+. When the locale is set to the default locale, the generic +index.html.erb+ view will be used. (Future Rails versions may well bring this _automagic_ localization to assets in +public+, etc.) @@ -561,7 +565,7 @@ I18n.translate :thanks, :name => 'Jeremy' # => 'Thanks Jeremy!' </ruby> -If a translation uses +:default+ or +:scope+ as an interpolation variable, an I+18n::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. +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. h4. Pluralization @@ -571,11 +575,14 @@ The +:count+ interpolation variable has a special role in that it both is interp <ruby> 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' </ruby> The algorithm for pluralizations in +:en+ is as simple as: diff --git a/guides/source/initialization.textile b/guides/source/initialization.textile index 43d89eac4b..b23f31cb1a 100644 --- a/guides/source/initialization.textile +++ b/guides/source/initialization.textile @@ -17,7 +17,7 @@ NOTE: Paths in this guide are relative to Rails or a Rails application unless ot TIP: If you want to follow along while browsing the Rails "source code":https://github.com/rails/rails, we recommend that you use the +t+ -key binding to open the file finder inside github and find files +key binding to open the file finder inside GitHub and find files quickly. h3. Launch! @@ -359,7 +359,7 @@ set earlier) is required. h4. +config/application+ When +require APP_PATH+ is executed, +config/application.rb+ is loaded. -This is a file exists in your app and it's free for you to change based +This file exists in your app and it's free for you to change based on your needs. h4. +Rails::Server#start+ @@ -453,7 +453,7 @@ end The interesting part for a Rails app is the last line, +server.run+. Here we encounter the +wrapped_app+ method again, which this time we're going to explore more (even though it was executed before, and -thus memoized by now). +thus memorized by now). <ruby> @wrapped_app ||= build_app app @@ -540,12 +540,12 @@ end </ruby> This is where all the Rails frameworks are loaded and thus made -available to the application. We wont go into detail of what happens +available to the application. We won't go into detail of what happens inside each of those frameworks, but you're encouraged to try and explore them on your own. For now, just keep in mind that common functionality like Rails engines, -I18n and Rails configuration is all bein defined here. +I18n and Rails configuration is all being defined here. h4. Back to +config/environment.rb+ @@ -657,10 +657,10 @@ def self.run(app, options={}) end </ruby> -We wont dig into the server configuration itself, but this is +We won't dig into the server configuration itself, but this is the last piece of our journey in the Rails initialization process. -This high level overview will help you understand when you code is +This high level overview will help you understand when your code is executed and how, and overall become a better Rails developer. If you still want to know more, the Rails source code itself is probably the best place to go next. diff --git a/guides/source/kindle/KINDLE.md b/guides/source/kindle/KINDLE.md index a7d9a4e4cf..08937e053e 100644 --- a/guides/source/kindle/KINDLE.md +++ b/guides/source/kindle/KINDLE.md @@ -9,16 +9,16 @@ ## Resources - * [StackOverflow: Kindle Periodical Format](http://stackoverflow.com/questions/5379565/kindle-periodical-format) + * [Stack Overflow: Kindle Periodical Format](http://stackoverflow.com/questions/5379565/kindle-periodical-format) * Example Periodical [.ncx](https://gist.github.com/808c971ed087b839d462) and [.opf](https://gist.github.com/d6349aa8488eca2ee6d0) - * [Kindle Publishing guidelines](http://kindlegen.s3.amazonaws.com/AmazonKindlePublishingGuidelines.pdf) + * [Kindle Publishing Guidelines](http://kindlegen.s3.amazonaws.com/AmazonKindlePublishingGuidelines.pdf) * [KindleGen & Kindle Previewer](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000234621) ## TODO ### Post release - * Integrate generated Kindle document in to published HTML guides + * Integrate generated Kindle document into published HTML guides * Tweak heading styles (most docs use h3/h4/h5, which end up being smaller than the text under it) * Tweak table styles (smaller text? Many of the tables are unusable on a Kindle in portrait mode) * Have the HTML/XML TOC 'drill down' into the TOCs of the individual guides diff --git a/guides/source/kindle/welcome.html.erb b/guides/source/kindle/welcome.html.erb index e30704c4e6..610a71570f 100644 --- a/guides/source/kindle/welcome.html.erb +++ b/guides/source/kindle/welcome.html.erb @@ -2,4 +2,4 @@ <h3>Kindle Edition</h3> -The Kindle Edition of the Rails Guides should be considered a work in progress. Feedback is really welcome, please see the "Feedback" section at the end of each guide for instructions. +The Kindle Edition of the Rails Guides should be considered a work in progress. Feedback is really welcome. Please see the "Feedback" section at the end of each guide for instructions. diff --git a/guides/source/layouts_and_rendering.textile b/guides/source/layouts_and_rendering.textile index 55bd521419..9eb8161c54 100644 --- a/guides/source/layouts_and_rendering.textile +++ b/guides/source/layouts_and_rendering.textile @@ -78,7 +78,7 @@ If we want to display the properties of all the books in our view, we can do so <td><%= book.content %></td> <td><%= link_to "Show", book %></td> <td><%= link_to "Edit", edit_book_path(book) %></td> - <td><%= link_to "Remove", book, :confirm => "Are you sure?", :method => :delete %></td> + <td><%= link_to "Remove", book, :method => :delete, :data => { :confirm => "Are you sure?" } %></td> </tr> <% end %> </table> @@ -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 342b5a4d57..e89bde7a83 100644 --- a/guides/source/migrations.textile +++ b/guides/source/migrations.textile @@ -14,7 +14,7 @@ Migrations also allow you to describe these transformations using Ruby. The great thing about this is that (like most of Active Record's functionality) it is database independent: you don't need to worry about the precise syntax of +CREATE TABLE+ any more than you worry about variations on +SELECT *+ (you can -drop down to raw SQL for database specific features). For example you could use +drop down to raw SQL for database specific features). For example, you could use SQLite3 in development, but MySQL in production. In this guide, you'll learn all about migrations including: @@ -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 <tt>change</tt> method. This method is preferred for writing constructive migrations (adding columns or @@ -111,6 +111,7 @@ Active Record provides methods that perform common data definition tasks in a database independent way (you'll read about them in detail later): * +add_column+ +* +add_reference+ * +add_index+ * +change_column+ * +change_table+ @@ -120,11 +121,12 @@ database independent way (you'll read about them in detail later): * +remove_column+ * +remove_index+ * +rename_column+ +* +remove_reference+ -If you need to perform tasks specific to your database (for example create a +If you need to perform tasks specific to your database (e.g., create a "foreign key":#active-record-and-referential-integrity constraint) then the +execute+ method allows you to execute arbitrary SQL. A migration is just a -regular Ruby class so you're not limited to these functions. For example after +regular Ruby class so you're not limited to these functions. For example, after adding a column you could write code to set the value of that column for existing records (if necessary using your models). @@ -163,7 +165,7 @@ config.active_record.timestamped_migrations = false The combination of timestamps and recording which migrations have been run allows Rails to handle common situations that occur with multiple developers. -For example Alice adds migrations +20080906120000+ and +20080906123000+ and Bob +For example, Alice adds migrations +20080906120000+ and +20080906123000+ and Bob adds +20080906124500+ and runs it. Alice finishes her changes and checks in her migrations and Bob pulls down the latest changes. When Bob runs +rake db:migrate+, Rails knows that it has not run Alice's two migrations so it executes the +up+ method for each migration. @@ -180,7 +182,7 @@ migration again: Rails thinks it has already run the migration and so will do nothing when you run +rake db:migrate+. You must rollback the migration (for example with +rake db:rollback+), edit your migration and then run +rake db:migrate+ to run the corrected version. -In general editing existing migrations is not a good idea: you will be creating +In general, editing existing migrations is not a good idea. You will be creating extra work for yourself and your co-workers and cause major headaches if the existing version of the migration has already been run on production machines. Instead, you should write a new migration that performs the changes you require. @@ -207,8 +209,7 @@ Active Record supports the following database column types: These will be mapped onto an appropriate underlying database type. For example, with MySQL the type +:string+ is mapped to +VARCHAR(255)+. You can create -columns of types not supported by Active Record when using the non-sexy syntax, -for example +columns of types not supported by Active Record when using the non-sexy syntax such as <ruby> create_table :products do |t| @@ -253,7 +254,7 @@ by Active Record). h4. Creating a Standalone Migration -If you are creating migrations for other purposes (for example to add a column +If you are creating migrations for other purposes (e.g., to add a column to an existing table) then you can also use the migration generator: <shell> @@ -307,7 +308,7 @@ class RemovePartNumberFromProducts < ActiveRecord::Migration end </ruby> -You are not limited to one magically generated column, for example +You are not limited to one magically generated column. For example <shell> $ rails generate migration AddDetailsToProducts part_number:string price:decimal @@ -332,6 +333,51 @@ NOTE: The generated migration file for destructive migrations will still be old-style using the +up+ and +down+ methods. This is because Rails needs to know the original data types defined when you made the original changes. +Also, the generator accepts column type as +references+(also available as +belongs_to+). For instance + +<shell> +$ rails generate migration AddUserRefToProducts user:references +</shell> + +generates + +<ruby> +class AddUserRefToProducts < ActiveRecord::Migration + def change + add_reference :products, :user, :index => true + end +end +</ruby> + +This migration will create a user_id column and appropriate index. + +h4. Supported Type Modifiers + +You can also specify some options just after the field type between curly braces. You can use the +following modifiers: + +* +limit+ Sets the maximum size of the +string/text/binary/integer+ fields +* +precision+ Defines the precision for the +decimal+ fields +* +scale+ Defines the scale for the +decimal+ fields +* +polymorphic+ Adds a +type+ column for +belongs_to+ associations + +For instance, running + +<shell> +$ rails generate migration AddDetailsToProducts price:decimal{5,2} supplier:references{polymorphic} +</shell> + +will produce a migration that looks like this + +<ruby> +class AddDetailsToProducts < ActiveRecord::Migration + def change + add_column :products, :price, :precision => 5, :scale => 2 + add_reference :products, :user, :polymorphic => true, :index => true + end +end +</ruby> + h3. Writing a Migration Once you have created your migration using one of the generators it's time to @@ -494,8 +540,8 @@ support":#active-record-and-referential-integrity. If the helpers provided by Active Record aren't enough you can use the +execute+ method to execute arbitrary SQL. -For more details and examples of individual methods, check the API documentation, -in particular the documentation for +For more details and examples of individual methods, check the API documentation. +In particular the documentation for "<tt>ActiveRecord::ConnectionAdapters::SchemaStatements</tt>":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html (which provides the methods available in the +up+ and +down+ methods), "<tt>ActiveRecord::ConnectionAdapters::TableDefinition</tt>":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html @@ -504,7 +550,7 @@ and "<tt>ActiveRecord::ConnectionAdapters::Table</tt>":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, @@ -597,7 +643,7 @@ down to, but not including, 20080906120000. h4. Rolling Back -A common task is to rollback the last migration, for example if you made a +A common task is to rollback the last migration. For example, if you made a mistake in it and wish to correct it. Rather than tracking down the version number associated with the previous migration you can run @@ -626,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. @@ -634,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 @@ -649,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 @@ -792,7 +838,7 @@ An error has occurred, this and all later migrations canceled: undefined method `fuzz' for #<Product:0x000001049b14a0> </plain> -A fix for this is to create a local model within the migration. This keeps rails +A fix for this is to create a local model within the migration. This keeps Rails from running the validations, so that the migrations run to completion. When using a faux model, it's a good idea to call @@ -866,14 +912,14 @@ If +:ruby+ is selected then the schema is stored in +db/schema.rb+. If you look at this file you'll find that it looks an awful lot like one very big migration: <ruby> -ActiveRecord::Schema.define(:version => 20080906171750) do - create_table "authors", :force => true do |t| +ActiveRecord::Schema.define(version: 20080906171750) do + create_table "authors", force: true do |t| t.string "name" t.datetime "created_at" t.datetime "updated_at" end - create_table "products", :force => true do |t| + create_table "products", force: true do |t| t.string "name" t.text "description" t.datetime "created_at" diff --git a/guides/source/performance_testing.textile b/guides/source/performance_testing.textile index 982fd1b070..6c5676c346 100644 --- a/guides/source/performance_testing.textile +++ b/guides/source/performance_testing.textile @@ -1,40 +1,55 @@ h2. Performance Testing Rails Applications -This guide covers the various ways of performance testing a Ruby on Rails application. By referring to this guide, you will be able to: - -* Understand the various types of benchmarking and profiling metrics -* Generate performance and benchmarking tests -* Install and use a GC-patched Ruby binary to measure memory usage and object allocation -* Understand the benchmarking information provided by Rails inside the log files -* Learn about various tools facilitating benchmarking and profiling - -Performance testing is an integral part of the development cycle. It is very important that you don't make your end users wait for too long before the page is completely loaded. Ensuring a pleasant browsing experience for end users and cutting the cost of unnecessary hardware is important for any non-trivial web application. +This guide covers the various ways of performance testing a Ruby on Rails +application. By referring to this guide, you will be able to: + +* Understand the various types of benchmarking and profiling metrics. +* Generate performance and benchmarking tests. +* Install and use a GC-patched Ruby binary to measure memory usage and object + allocation. +* Understand the benchmarking information provided by Rails inside the log files. +* Learn about various tools facilitating benchmarking and profiling. + +Performance testing is an integral part of the development cycle. It is very +important that you don't make your end users wait for too long before the page +is completely loaded. Ensuring a pleasant browsing experience for end users and +cutting the cost of unnecessary hardware is important for any non-trivial web +application. endprologue. h3. Performance Test Cases -Rails performance tests are a special type of integration tests, designed for benchmarking and profiling the test code. With performance tests, you can determine where your application's memory or speed problems are coming from, and get a more in-depth picture of those problems. +Rails performance tests are a special type of integration tests, designed for +benchmarking and profiling the test code. With performance tests, you can +determine where your application's memory or speed problems are coming from, +and get a more in-depth picture of those problems. -In a freshly generated Rails application, +test/performance/browsing_test.rb+ contains an example of a performance test: +In a freshly generated Rails application, +test/performance/browsing_test.rb+ +contains an example of a performance test: <ruby> require 'test_helper' require 'rails/performance_test_help' -# Profiling results for each test method are written to tmp/performance. class BrowsingTest < ActionDispatch::PerformanceTest - def test_homepage + # Refer to the documentation for all available options + # self.profile_options = { runs: 5, metrics: [:wall_time, :memory], + # output: 'tmp/performance', formats: [:flat] } + + test "homepage" do get '/' end end </ruby> -This example is a simple performance test case for profiling a GET request to the application's homepage. +This example is a simple performance test case for profiling a GET request to +the application's homepage. h4. Generating Performance Tests -Rails provides a generator called +performance_test+ for creating new performance tests: +Rails provides a generator called +performance_test+ for creating new +performance tests: <shell> $ rails generate performance_test homepage @@ -47,8 +62,11 @@ require 'test_helper' require 'rails/performance_test_help' class HomepageTest < ActionDispatch::PerformanceTest - # Replace this with your real tests. - def test_homepage + # Refer to the documentation for all available options + # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory], + # :output => 'tmp/performance', :formats => [:flat] } + + test "homepage" do get '/' end end @@ -60,7 +78,7 @@ Let's assume your application has the following controller and model: <ruby> # routes.rb -root :to => 'home#index' +root to: 'home#dashboard' resources :posts # home_controller.rb @@ -97,9 +115,11 @@ end h5. Controller Example -Because performance tests are a special kind of integration test, you can use the +get+ and +post+ methods in them. +Because performance tests are a special kind of integration test, you can use +the +get+ and +post+ methods in them. -Here's the performance test for +HomeController#dashboard+ and +PostsController#create+: +Here's the performance test for +HomeController#dashboard+ and ++PostsController#create+: <ruby> require 'test_helper' @@ -111,21 +131,24 @@ class PostPerformanceTest < ActionDispatch::PerformanceTest login_as(:lifo) end - def test_homepage + test "homepage" do get '/dashboard' end - def test_creating_new_post - post '/posts', :post => { :body => 'lifo is fooling you' } + test "creating new post" do + post '/posts', post: { body: 'lifo is fooling you' } end end </ruby> -You can find more details about the +get+ and +post+ methods in the "Testing Rails Applications":testing.html guide. +You can find more details about the +get+ and +post+ methods in the +"Testing Rails Applications":testing.html guide. h5. Model Example -Even though the performance tests are integration tests and hence closer to the request/response cycle by nature, you can still performance test pure model code. +Even though the performance tests are integration tests and hence closer to +the request/response cycle by nature, you can still performance test pure model +code. Performance test for +Post+ model: @@ -134,11 +157,11 @@ require 'test_helper' require 'rails/performance_test_help' class PostModelTest < ActionDispatch::PerformanceTest - def test_creation - Post.create :body => 'still fooling you', :cost => '100' + test "creation" do + Post.create body: 'still fooling you', cost: '100' end - def test_slow_method + test "slow method" do # Using posts(:awesome) fixture posts(:awesome).slow_method end @@ -151,7 +174,8 @@ Performance tests can be run in two modes: Benchmarking and Profiling. h5. Benchmarking -Benchmarking makes it easy to quickly gather a few metrics about each test run. By default, each test case is run *4 times* in benchmarking mode. +Benchmarking makes it easy to quickly gather a few metrics about each test run. +By default, each test case is run *4 times* in benchmarking mode. To run performance tests in benchmarking mode: @@ -161,7 +185,10 @@ $ rake test:benchmark h5. Profiling -Profiling allows you to make an in-depth analysis of each of your tests by using an external profiler. Depending on your Ruby interpreter, this profiler can be native (Rubinius, JRuby) or not (MRI, which uses RubyProf). By default, each test case is run *once* in profiling mode. +Profiling allows you to make an in-depth analysis of each of your tests by using +an external profiler. Depending on your Ruby interpreter, this profiler can be +native (Rubinius, JRuby) or not (MRI, which uses RubyProf). By default, each +test case is run *once* in profiling mode. To run performance tests in profiling mode: @@ -171,23 +198,33 @@ $ rake test:profile h4. Metrics -Benchmarking and profiling run performance tests and give you multiple metrics. The availability of each metric is determined by the interpreter being used—none of them support all metrics—and by the mode in use. A brief description of each metric and their availability across interpreters/modes is given below. +Benchmarking and profiling run performance tests and give you multiple metrics. +The availability of each metric is determined by the interpreter being used—none +of them support all metrics—and by the mode in use. A brief description of each +metric and their availability across interpreters/modes is given below. h5. Wall Time -Wall time measures the real world time elapsed during the test run. It is affected by any other processes concurrently running on the system. +Wall time measures the real world time elapsed during the test run. It is +affected by any other processes concurrently running on the system. h5. Process Time -Process time measures the time taken by the process. It is unaffected by any other processes running concurrently on the same system. Hence, process time is likely to be constant for any given performance test, irrespective of the machine load. +Process time measures the time taken by the process. It is unaffected by any +other processes running concurrently on the same system. Hence, process time +is likely to be constant for any given performance test, irrespective of the +machine load. h5. CPU Time -Similar to process time, but leverages the more accurate CPU clock counter available on the Pentium and PowerPC platforms. +Similar to process time, but leverages the more accurate CPU clock counter +available on the Pentium and PowerPC platforms. h5. User Time -User time measures the amount of time the CPU spent in user-mode, i.e. within the process. This is not affected by other processes and by the time it possibly spends blocked. +User time measures the amount of time the CPU spent in user-mode, i.e. within +the process. This is not affected by other processes and by the time it possibly +spends blocked. h5. Memory @@ -223,11 +260,13 @@ h6(#profiling_1). Profiling |_.Rubinius | yes | no | no | no | no | no | no | no | |_.JRuby | yes | no | no | no | no | no | no | no | -NOTE: To profile under JRuby you'll need to run +export JRUBY_OPTS="-Xlaunch.inproc=false --profile.api"+ *before* the performance tests. +NOTE: To profile under JRuby you'll need to run +export JRUBY_OPTS="-Xlaunch.inproc=false --profile.api"+ +*before* the performance tests. h4. Understanding the Output -Performance tests generate different outputs inside +tmp/performance+ directory depending on their mode and metric. +Performance tests generate different outputs inside +tmp/performance+ directory +depending on their mode and metric. h5(#output-benchmarking). Benchmarking @@ -248,7 +287,9 @@ BrowsingTest#test_homepage (31 ms warmup) h6. CSV Files -Performance test results are also appended to +.csv+ files inside +tmp/performance+. For example, running the default +BrowsingTest#test_homepage+ will generate following five files: +Performance test results are also appended to +.csv+ files inside +tmp/performance+. +For example, running the default +BrowsingTest#test_homepage+ will generate +following five files: * BrowsingTest#test_homepage_gc_runs.csv * BrowsingTest#test_homepage_gc_time.csv @@ -256,7 +297,9 @@ Performance test results are also appended to +.csv+ files inside +tmp/performan * BrowsingTest#test_homepage_objects.csv * BrowsingTest#test_homepage_wall_time.csv -As the results are appended to these files each time the performance tests are run in benchmarking mode, you can collect data over a period of time. This can be very helpful in analyzing the effects of code changes. +As the results are appended to these files each time the performance tests are +run in benchmarking mode, you can collect data over a period of time. This can +be very helpful in analyzing the effects of code changes. Sample output of +BrowsingTest#test_homepage_wall_time.csv+: @@ -276,7 +319,10 @@ measurement,created_at,app,rails,ruby,platform h5(#output-profiling). Profiling -In profiling mode, performance tests can generate multiple types of outputs. The command line output is always presented but support for the others is dependent on the interpreter in use. A brief description of each type and their availability across interpreters is given below. +In profiling mode, performance tests can generate multiple types of outputs. +The command line output is always presented but support for the others is +dependent on the interpreter in use. A brief description of each type and +their availability across interpreters is given below. h6. Command Line @@ -291,15 +337,18 @@ BrowsingTest#test_homepage (58 ms warmup) h6. Flat -Flat output shows the metric—time, memory, etc—measure in each method. "Check Ruby-Prof documentation for a better explanation":http://ruby-prof.rubyforge.org/files/examples/flat_txt.html. +Flat output shows the metric—time, memory, etc—measure in each method. +"Check Ruby-Prof documentation for a better explanation":http://ruby-prof.rubyforge.org/files/examples/flat_txt.html. h6. Graph -Graph output shows the metric measure in each method, which methods call it and which methods it calls. "Check Ruby-Prof documentation for a better explanation":http://ruby-prof.rubyforge.org/files/examples/graph_txt.html. +Graph output shows the metric measure in each method, which methods call it and +which methods it calls. "Check Ruby-Prof documentation for a better explanation":http://ruby-prof.rubyforge.org/files/examples/graph_txt.html. h6. Tree -Tree output is profiling information in calltree format for use by "kcachegrind":http://kcachegrind.sourceforge.net/html/Home.html and similar tools. +Tree output is profiling information in calltree format for use by "kcachegrind":http://kcachegrind.sourceforge.net/html/Home.html +and similar tools. h6. Output Availability @@ -311,24 +360,24 @@ h6. Output Availability h4. Tuning Test Runs -Test runs can be tuned by setting the +profile_options+ class variable on your test class. +Test runs can be tuned by setting the +profile_options+ class variable on your +test class. <ruby> require 'test_helper' require 'rails/performance_test_help' -# Profiling results for each test method are written to tmp/performance. class BrowsingTest < ActionDispatch::PerformanceTest - self.profile_options = { :runs => 5, - :metrics => [:wall_time, :memory] } + self.profile_options = { runs: 5, metrics: [:wall_time, :memory] } - def test_homepage + test "homepage" get '/' end end </ruby> -In this example, the test would run 5 times and measure wall time and memory. There are a few configurable options: +In this example, the test would run 5 times and measure wall time and memory. +There are a few configurable options: |_.Option |_.Description|_.Default|_.Mode| |+:runs+ |Number of runs.|Benchmarking: 4, Profiling: 1|Both| @@ -346,11 +395,13 @@ Metrics and formats have different defaults depending on the interpreter in use. |/2.JRuby |Benchmarking|+[:wall_time, :user_time, :memory, :gc_runs, :gc_time]+|N/A| |Profiling |+[:wall_time]+|+[:flat, :graph]+| -As you've probably noticed by now, metrics and formats are specified using a symbol array with each name "underscored.":http://api.rubyonrails.org/classes/String.html#method-i-underscore +As you've probably noticed by now, metrics and formats are specified using a +symbol array with each name "underscored.":http://api.rubyonrails.org/classes/String.html#method-i-underscore h4. Performance Test Environment -Performance tests are run in the +test+ environment. But running performance tests will set the following configuration parameters: +Performance tests are run in the +test+ environment. But running performance +tests will set the following configuration parameters: <shell> ActionController::Base.perform_caching = true @@ -358,11 +409,13 @@ ActiveSupport::Dependencies.mechanism = :require Rails.logger.level = ActiveSupport::BufferedLogger::INFO </shell> -As +ActionController::Base.perform_caching+ is set to +true+, performance tests will behave much as they do in the +production+ environment. +As +ActionController::Base.perform_caching+ is set to +true+, performance tests +will behave much as they do in the +production+ environment. h4. Installing GC-Patched MRI -To get the best from Rails' performance tests under MRI, you'll need to build a special Ruby binary with some super powers. +To get the best from Rails' performance tests under MRI, you'll need to build +a special Ruby binary with some super powers. The recommended patches for each MRI version are: @@ -371,13 +424,18 @@ The recommended patches for each MRI version are: |1.8.7|ruby187gc| |1.9.2 and above|gcdata| -All of these can be found on "RVM's _patches_ directory":https://github.com/wayneeseguin/rvm/tree/master/patches/ruby under each specific interpreter version. +All of these can be found on "RVM's _patches_ directory":https://github.com/wayneeseguin/rvm/tree/master/patches/ruby +under each specific interpreter version. -Concerning the installation itself, you can either do this easily by using "RVM":http://rvm.beginrescueend.com or you can build everything from source, which is a little bit harder. +Concerning the installation itself, you can either do this easily by using +"RVM":http://rvm.beginrescueend.com or you can build everything from source, +which is a little bit harder. h5. Install Using RVM -The process of installing a patched Ruby interpreter is very easy if you let RVM do the hard work. All of the following RVM commands will provide you with a patched Ruby interpreter: +The process of installing a patched Ruby interpreter is very easy if you let RVM +do the hard work. All of the following RVM commands will provide you with a +patched Ruby interpreter: <shell> $ rvm install 1.9.2-p180 --patch gcdata @@ -385,7 +443,8 @@ $ rvm install 1.8.7 --patch ruby187gc $ rvm install 1.9.2-p180 --patch ~/Downloads/downloaded_gcdata_patch.patch </shell> -You can even keep your regular interpreter by assigning a name to the patched one: +You can even keep your regular interpreter by assigning a name to the patched +one: <shell> $ rvm install 1.9.2-p180 --patch gcdata --name gcdata @@ -397,7 +456,9 @@ And it's done! You have installed a patched Ruby interpreter. h5. Install From Source -This process is a bit more complicated, but straightforward nonetheless. If you've never compiled a Ruby binary before, follow these steps to build a Ruby binary inside your home directory. +This process is a bit more complicated, but straightforward nonetheless. If +you've never compiled a Ruby binary before, follow these steps to build a +Ruby binary inside your home directory. h6. Download and Extract @@ -417,7 +478,9 @@ $ curl http://github.com/wayneeseguin/rvm/raw/master/patches/ruby/1.8.7/ruby187g h6. Configure and Install -The following will install Ruby in your home directory's +/rubygc+ directory. Make sure to replace +<homedir>+ with a full patch to your actual home directory. +The following will install Ruby in your home directory's +/rubygc+ directory. +Make sure to replace +<homedir>+ with a full patch to your actual home +directory. <shell> $ ./configure --prefix=/<homedir>/rubygc @@ -438,23 +501,22 @@ alias gcrails='~/rubygc/bin/rails' Don't forget to use your aliases from now on. -h6. Install RubyGems (1.8 only!) - -Download "RubyGems":http://rubyforge.org/projects/rubygems and install it from source. Rubygem's README file should have necessary installation instructions. Please note that this step isn't necessary if you've installed Ruby 1.9 and above. - h4. Using Ruby-Prof on MRI and REE -Add Ruby-Prof to your applications' Gemfile if you want to benchmark/profile under MRI or REE: +Add Ruby-Prof to your applications' Gemfile if you want to benchmark/profile +under MRI or REE: <ruby> -gem 'ruby-prof', :git => 'git://github.com/wycats/ruby-prof.git' +gem 'ruby-prof', git: 'git://github.com/wycats/ruby-prof.git' </ruby> Now run +bundle install+ and you're ready to go. h3. Command Line Tools -Writing performance test cases could be an overkill when you are looking for one time tests. Rails ships with two command line tools that enable quick and dirty performance testing: +Writing performance test cases could be an overkill when you are looking for one +time tests. Rails ships with two command line tools that enable quick and dirty +performance testing: h4. +benchmarker+ @@ -498,11 +560,14 @@ Example: $ rails profiler 'Item.all' 'CouchItem.all' --runs 2 --metrics process_time --formats flat </shell> -NOTE: Metrics and formats vary from interpreter to interpreter. Pass +--help+ to each tool to see the defaults for your interpreter. +NOTE: Metrics and formats vary from interpreter to interpreter. Pass +--help+ to +each tool to see the defaults for your interpreter. h3. Helper Methods -Rails provides various helper methods inside Active Record, Action Controller and Action View to measure the time taken by a given piece of code. The method is called +benchmark()+ in all the three components. +Rails provides various helper methods inside Active Record, Action Controller +and Action View to measure the time taken by a given piece of code. The method +is called +benchmark()+ in all the three components. h4. Model @@ -514,17 +579,19 @@ Project.benchmark("Creating project") do end </ruby> -This benchmarks the code enclosed in the +Project.benchmark("Creating project") do...end+ block and prints the result to the log file: +This benchmarks the code enclosed in the +Project.benchmark("Creating project") do...end+ +block and prints the result to the log file: <ruby> Creating project (185.3ms) </ruby> -Please refer to the "API docs":http://api.rubyonrails.org/classes/ActiveRecord/Base.html#M001336 for additional options to +benchmark()+ +Please refer to the "API docs":http://api.rubyonrails.org/classes/ActiveSupport/Benchmarkable.html#method-i-benchmark +for additional options to +benchmark()+. h4. Controller -Similarly, you could use this helper method inside "controllers":http://api.rubyonrails.org/classes/ActiveSupport/Benchmarkable.html +Similarly, you could use this helper method inside "controllers.":http://api.rubyonrails.org/classes/ActiveSupport/Benchmarkable.html <ruby> def process_projects @@ -535,7 +602,7 @@ def process_projects end </ruby> -NOTE: +benchmark+ is a class method inside controllers +NOTE: +benchmark+ is a class method inside controllers. h4. View @@ -549,7 +616,8 @@ And in "views":http://api.rubyonrails.org/classes/ActiveSupport/Benchmarkable.ht h3. Request Logging -Rails log files contain very useful information about the time taken to serve each request. Here's a typical log file entry: +Rails log files contain very useful information about the time taken to serve +each request. Here's a typical log file entry: <shell> Processing ItemsController#index (for 127.0.0.1 at 2009-01-08 03:06:39) [GET] @@ -564,9 +632,14 @@ For this section, we're only interested in the last line: Completed in 5ms (View: 2, DB: 0) | 200 OK [http://0.0.0.0/items] </shell> -This data is fairly straightforward to understand. Rails uses millisecond(ms) as the metric to measure the time taken. The complete request spent 5 ms inside Rails, out of which 2 ms were spent rendering views and none was spent communication with the database. It's safe to assume that the remaining 3 ms were spent inside the controller. +This data is fairly straightforward to understand. Rails uses millisecond(ms) as +the metric to measure the time taken. The complete request spent 5 ms inside +Rails, out of which 2 ms were spent rendering views and none was spent +communication with the database. It's safe to assume that the remaining 3 ms +were spent inside the controller. -Michael Koziarski has an "interesting blog post":http://www.therailsway.com/2009/1/6/requests-per-second explaining the importance of using milliseconds as the metric. +Michael Koziarski has an "interesting blog post":http://www.therailsway.com/2009/1/6/requests-per-second +explaining the importance of using milliseconds as the metric. h3. Useful Links @@ -576,6 +649,7 @@ h4. Rails Plugins and Gems * "Palmist":http://www.flyingmachinestudios.com/programming/announcing-palmist * "Rails Footnotes":https://github.com/josevalim/rails-footnotes/tree/master * "Query Reviewer":https://github.com/dsboulder/query_reviewer/tree/master +* "MiniProfiler":http://www.miniprofiler.com h4. Generic Tools @@ -587,11 +661,12 @@ h4. Generic Tools h4. Tutorials and Documentation * "ruby-prof API Documentation":http://ruby-prof.rubyforge.org -* "Request Profiling Railscast":http://railscasts.com/episodes/98-request-profiling - Outdated, but useful for understanding call graphs +* "Request Profiling Railscast":http://railscasts.com/episodes/98-request-profiling - Outdated, but useful for understanding call graphs. h3. Commercial Products -Rails has been lucky to have a few companies dedicated to Rails-specific performance tools. A couple of those are: +Rails has been lucky to have a few companies dedicated to Rails-specific +performance tools. A couple of those are: * "New Relic":http://www.newrelic.com * "Scout":http://scoutapp.com diff --git a/guides/source/plugins.textile b/guides/source/plugins.textile index 95e38db483..50ea6b166a 100644 --- a/guides/source/plugins.textile +++ b/guides/source/plugins.textile @@ -13,7 +13,7 @@ After reading this guide you should be familiar with: This guide describes how to build a test-driven plugin that will: -* Extend core ruby classes like Hash and String +* Extend core Ruby classes like Hash and String * Add methods to ActiveRecord::Base in the tradition of the 'acts_as' plugins * Give you information about where to put generators in your plugin. @@ -28,7 +28,7 @@ h3. Setup _"vendored plugins"_ were available in previous versions of Rails, but they are deprecated in Rails 3.2, and will not be available in the future. -Currently, Rails plugins are built as gems, _gemified plugins_. They can be shared accross +Currently, Rails plugins are built as gems, _gemified plugins_. They can be shared across different rails applications using RubyGems and Bundler if desired. h4. Generate a gemified plugin. @@ -174,11 +174,11 @@ require 'test_helper' class ActsAsYaffleTest < Test::Unit::TestCase def test_a_hickwalls_yaffle_text_field_should_be_last_squawk - assert_equal :last_squawk, Hickwall.yaffle_text_field + assert_equal "last_squawk", Hickwall.yaffle_text_field end def test_a_wickwalls_yaffle_text_field_should_be_last_tweet - assert_equal :last_tweet, Wickwall.yaffle_text_field + assert_equal "last_tweet", Wickwall.yaffle_text_field end end @@ -392,7 +392,7 @@ the creation of generators can be found in the "Generators Guide":generators.htm h3. Publishing your Gem Gem plugins currently in development can easily be shared from any Git repository. To share the Yaffle gem with others, simply -commit the code to a Git repository (like Github) and add a line to the Gemfile of the application in question: +commit the code to a Git repository (like GitHub) and add a line to the Gemfile of the application in question: <ruby> gem 'yaffle', :git => 'git://github.com/yaffle_watcher/yaffle.git' @@ -401,7 +401,7 @@ gem 'yaffle', :git => 'git://github.com/yaffle_watcher/yaffle.git' After running +bundle install+, your gem functionality will be available to the application. When the gem is ready to be shared as a formal release, it can be published to "RubyGems":http://www.rubygems.org. -For more information about publishing gems to RubyGems, see: "http://blog.thepete.net/2010/11/creating-and-publishing-your-first-ruby.html":http://blog.thepete.net/2010/11/creating-and-publishing-your-first-ruby.html +For more information about publishing gems to RubyGems, see: "Creating and Publishing Your First Ruby Gem":http://blog.thepete.net/2010/11/creating-and-publishing-your-first-ruby.html h3. RDoc Documentation @@ -414,7 +414,7 @@ The first step is to update the README file with detailed information about how * How to add the functionality to the app (several examples of common use cases) * Warnings, gotchas or tips that might help users and save them time -Once your README is solid, go through and add rdoc comments to all of the methods that developers will use. It's also customary to add '#:nodoc:' comments to those parts of the code that are not included in the public api. +Once your README is solid, go through and add rdoc comments to all of the methods that developers will use. It's also customary to add '#:nodoc:' comments to those parts of the code that are not included in the public API. Once your comments are good to go, navigate to your plugin directory and run: @@ -425,6 +425,6 @@ $ rake rdoc h4. References * "Developing a RubyGem using Bundler":https://github.com/radar/guides/blob/master/gem-development.md -* "Using Gemspecs As Intended":http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended/ +* "Using .gemspecs as Intended":http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended/ * "Gemspec Reference":http://docs.rubygems.org/read/chapter/20 -* "GemPlugins":http://www.mbleigh.com/2008/06/11/gemplugins-a-brief-introduction-to-the-future-of-rails-plugins +* "GemPlugins: A Brief Introduction to the Future of Rails Plugins":http://www.intridea.com/blog/2008/6/11/gemplugins-a-brief-introduction-to-the-future-of-rails-plugins diff --git a/guides/source/rails_application_templates.textile b/guides/source/rails_application_templates.textile index f50ced3307..2fa40bc4cc 100644 --- a/guides/source/rails_application_templates.textile +++ b/guides/source/rails_application_templates.textile @@ -38,7 +38,7 @@ rake("db:migrate") git :init git :add => "." -git :commit => "-a -m 'Initial commit'" +git :commit => %Q{ -m 'Initial commit' } </ruby> The following sections outlines the primary methods provided by the API: diff --git a/guides/source/routing.textile b/guides/source/routing.textile index cffbf9bec4..9718887612 100644 --- a/guides/source/routing.textile +++ b/guides/source/routing.textile @@ -273,6 +273,36 @@ The corresponding route helper would be +publisher_magazine_photo_url+, requirin TIP: _Resources should never be nested more than 1 level deep._ +h4. Routing concerns + +Routing Concerns allows you to declare common routes that can be reused inside others resources and routes. + +<ruby> +concern :commentable do + resources :comments +end + +concern :image_attachable do + resources :images, only: :index +end +</ruby> + +These concerns can be used in resources to avoid code duplication and share behavior across routes. + +<ruby> +resources :messages, concerns: :commentable + +resources :posts, concerns: [:commentable, :image_attachable] +</ruby> + +Also you can use them in any place that you want inside the routes, for example in a scope or namespace call: + +<ruby> +namespace :posts do + concerns :commentable +end +</ruby> + h4. Creating Paths and URLs From Objects In addition to using the routing helpers, Rails can also create paths and URLs from an array of parameters. For example, suppose you have this set of routes: @@ -614,10 +644,10 @@ You can also reuse dynamic segments from the match in the path to redirect to: get "/stories/:name" => redirect("/posts/%{name}") </ruby> -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: <ruby> -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}" } </ruby> diff --git a/guides/source/security.textile b/guides/source/security.textile index 626d6fa508..4c6c78a353 100644 --- a/guides/source/security.textile +++ b/guides/source/security.textile @@ -81,9 +81,7 @@ This will also be a good idea, if you modify the structure of an object and old h4. Session Storage -NOTE: _Rails provides several storage mechanisms for the session hashes. The most important are +ActiveRecord::SessionStore+ and +ActionDispatch::Session::CookieStore+._ - -There are a number of session storages, i.e. where Rails saves the session hash and session id. Most real-live applications choose ActiveRecord::SessionStore (or one of its derivatives) over file storage due to performance and maintenance reasons. ActiveRecord::SessionStore keeps the session id and hash in a database table and saves and retrieves the hash on every request. +NOTE: _Rails provides several storage mechanisms for the session hashes. The most important is +ActionDispatch::Session::CookieStore+._ Rails 2 introduced a new default session storage, CookieStore. CookieStore saves the session hash directly in a cookie on the client-side. The server retrieves the session hash from the cookie and eliminates the need for a session id. That will greatly increase the speed of the application, but it is a controversial storage option and you have to think about the security implications of it: @@ -608,7 +606,7 @@ This URL passes the filter because the regular expression matches – the second link_to "Homepage", @user.homepage </ruby> -The link looks innocent to visitors, but when it's clicked, it will execute the javascript function "exploit_code" or any other javascript the attacker provides. +The link looks innocent to visitors, but when it's clicked, it will execute the JavaScript function "exploit_code" or any other JavaScript the attacker provides. To fix the regular expression, \A and \z should be used instead of ^ and $, like so: @@ -851,7 +849,7 @@ Network traffic is mostly based on the limited Western alphabet, so new characte &#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;> </html> -This example pops up a message box. It will be recognized by the above sanitize() filter, though. A great tool to obfuscate and encode strings, and thus “get to know your enemy”, is the "Hackvertor":http://www.businessinfo.co.uk/labs/hackvertor/hackvertor.php. Rails' sanitize() method does a good job to fend off encoding attacks. +This example pops up a message box. It will be recognized by the above sanitize() filter, though. A great tool to obfuscate and encode strings, and thus “get to know your enemy”, is the "Hackvertor":https://hackvertor.co.uk/public. Rails' sanitize() method does a good job to fend off encoding attacks. h5. Examples from the Underground @@ -1021,6 +1019,47 @@ Content-Type: text/html Under certain circumstances this would present the malicious HTML to the victim. However, this only seems to work with Keep-Alive connections (and many browsers are using one-time connections). But you can't rely on this. _(highlight)In any case this is a serious bug, and you should update your Rails to version 2.0.5 or 2.1.2 to eliminate Header Injection (and thus response splitting) risks._ +h3. Default Headers + +Every HTTP response from your Rails application receives the following default security headers. + +<ruby> +config.action_dispatch.default_headers = { + 'X-Frame-Options' => 'SAMEORIGIN', + 'X-XSS-Protection' => '1; mode=block', + 'X-Content-Type-Options' => 'nosniff' +} +</ruby> + +You can configure default headers in <ruby>config/application.rb</ruby>. + +<ruby> +config.action_dispatch.default_headers = { + 'Header-Name' => 'Header-Value', + 'X-Frame-Options' => 'DENY' +} +</ruby> + +Or you can remove them. + +<ruby> +config.action_dispatch.default_headers.clear +</ruby> + +Here is the list of common headers: +* X-Frame-Options +_'SAMEORIGIN' in Rails by default_ - allow framing on same domain. Set it to 'DENY' to deny framing at all or 'ALLOWALL' if you want to allow framing for all website. +* X-XSS-Protection +_'1; mode=block' in Rails by default_ - use XSS Auditor and block page if XSS attack is detected. Set it to '0;' if you want to switch XSS Auditor off(useful if response contents scripts from request parameters) +* X-Content-Type-Options +_'nosniff' in Rails by default_ - stops the browser from guessing the MIME type of a file. +* X-Content-Security-Policy +"A powerful mechanism for controlling which sites certain content types can be loaded from":http://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html +* Access-Control-Allow-Origin +Used to control which sites are allowed to bypass same origin policies and send cross-origin requests. +* Strict-Transport-Security +"Used to control if the browser is allowed to only access a site over a secure connection":http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security + h3. Additional Resources The security landscape shifts and it is important to keep up to date, because missing a new vulnerability can be catastrophic. You can find additional resources about (Rails) security here: diff --git a/guides/source/testing.textile b/guides/source/testing.textile index d35be6a70e..4faf59fad8 100644 --- a/guides/source/testing.textile +++ b/guides/source/testing.textile @@ -1,62 +1,57 @@ h2. A Guide to Testing Rails Applications -This guide covers built-in mechanisms offered by Rails to test your application. By referring to this guide, you will be able to: +This guide covers built-in mechanisms offered by Rails to test your +application. By referring to this guide, you will be able to: * Understand Rails testing terminology -* Write unit, functional and integration tests for your application +* Write unit, functional, and integration tests for your application * Identify other popular testing approaches and plugins -This guide won't teach you to write a Rails application; it assumes basic familiarity with the Rails way of doing things. - endprologue. h3. Why Write Tests for your Rails Applications? -* Rails makes it super easy to write your tests. It starts by producing skeleton test code in the background while you are creating your models and controllers. -* By simply running your Rails tests you can ensure your code adheres to the desired functionality even after some major code refactoring. -* Rails tests can also simulate browser requests and thus you can test your application's response without having to test it through your browser. - -h3. Introduction to Testing - -Testing support was woven into the Rails fabric from the beginning. It wasn't an "oh! let's bolt on support for running tests because they're new and cool" epiphany. Just about every Rails application interacts heavily with a database - and, as a result, your tests will need a database to interact with as well. To write efficient tests, you'll need to understand how to set up this database and populate it with sample data. +Rails makes it super easy to write your tests. It starts by producing skeleton test code while you are creating your models and controllers. -h4. The Three Environments +By simply running your Rails tests you can ensure your code adheres to the desired functionality even after some major code refactoring. -Every Rails application you build has 3 sides: a side for production, a side for development, and a side for testing. +Rails tests can also simulate browser requests and thus you can test your application's response without having to test it through your browser. -One place you'll find this distinction is in the +config/database.yml+ file. This YAML configuration file has 3 different sections defining 3 unique database setups: +h3. Introduction to Testing -* production -* development -* test +Testing support was woven into the Rails fabric from the beginning. It wasn't an "oh! let's bolt on support for running tests because they're new and cool" epiphany. Just about every Rails application interacts heavily with a database and, as a result, your tests will need a database to interact with as well. To write efficient tests, you'll need to understand how to set up this database and populate it with sample data. -This allows you to set up and interact with test data without any danger of your tests altering data from your production environment. +h4. The Test Environment -For example, suppose you need to test your new +delete_this_user_and_every_everything_associated_with_it+ function. Wouldn't you want to run this in an environment where it makes no difference if you destroy data or not? +By default, every Rails application has three environments: development, test, and production. The database for each one of them is configured in +config/database.yml+. -When you do end up destroying your testing database (and it will happen, trust me), you can rebuild it from scratch according to the specs defined in the development database. You can do this by running +rake db:test:prepare+. +A dedicated test database allows you to set up and interact with test data in isolation. Tests can mangle test data with confidence, that won't touch the data in the development or production databases. h4. Rails Sets up for Testing from the Word Go Rails creates a +test+ folder for you as soon as you create a Rails project using +rails new+ _application_name_. If you list the contents of this folder then you shall see: <shell> -$ ls -F test/ +$ ls -F test -fixtures/ functional/ integration/ test_helper.rb unit/ +fixtures/ functional/ integration/ performance/ test_helper.rb unit/ </shell> -The +unit+ folder is meant to hold tests for your models, the +functional+ folder is meant to hold tests for your controllers, and the +integration+ folder is meant to hold tests that involve any number of controllers interacting. Fixtures are a way of organizing test data; they reside in the +fixtures+ folder. The +test_helper.rb+ file holds the default configuration for your tests. +The +unit+ directory is meant to hold tests for your models, the +functional+ directory is meant to hold tests for your controllers, the +integration+ directory is meant to hold tests that involve any number of controllers interacting, and the +performance+ directory is meant for performance tests. + +Fixtures are a way of organizing test data; they reside in the +fixtures+ folder. + +The +test_helper.rb+ file holds the default configuration for your tests. h4. The Low-Down on Fixtures For good tests, you'll need to give some thought to setting up test data. In Rails, you can handle this by defining and customizing fixtures. -h5. What are Fixtures? +h5. What Are Fixtures? -_Fixtures_ is a fancy word for sample data. Fixtures allow you to populate your testing database with predefined data before your tests run. Fixtures are database independent and assume a single format: *YAML*. +_Fixtures_ is a fancy word for sample data. Fixtures allow you to populate your testing database with predefined data before your tests run. Fixtures are database independent written in YAML. There is one file per model. -You'll find fixtures under your +test/fixtures+ directory. When you run +rails generate model+ to create a new model, fixture stubs will be automatically created and placed in this directory. +You'll find fixtures under your +test/fixtures+ directory. When you run +rails generate model+ to create a new model fixture stubs will be automatically created and placed in this directory. h5. YAML @@ -77,35 +72,20 @@ steve: profession: guy with keyboard </yaml> -Each fixture is given a name followed by an indented list of colon-separated key/value pairs. Records are separated by a blank space. You can place comments in a fixture file by using the # character in the first column. +Each fixture is given a name followed by an indented list of colon-separated key/value pairs. Records are typically separated by a blank space. You can place comments in a fixture file by using the # character in the first column. h5. ERB'in It Up -ERB allows you to embed ruby code within templates. YAML fixture format is pre-processed with ERB when you load fixtures. This allows you to use Ruby to help you generate some sample data. - -<erb> -<% earth_size = 20 %> -mercury: - size: <%= earth_size / 50 %> - brightest_on: <%= 113.days.ago.to_s(:db) %> - -venus: - size: <%= earth_size / 2 %> - brightest_on: <%= 67.days.ago.to_s(:db) %> - -mars: - size: <%= earth_size - 69 %> - brightest_on: <%= 13.days.from_now.to_s(:db) %> -</erb> - -Anything encased within the +ERB allows you to embed Ruby code within templates. The YAML fixture format is pre-processed with ERB when Rails loads fixtures. This allows you to use Ruby to help you generate some sample data. For example, the following code generates a thousand users: <erb> -<% %> +<% 1000.times do |n| %> +user_<%= n %>: + username: <%= "user%03d" % n %> + email: <%= "user%03d@example.com" % n %> +<% end %> </erb> -tag is considered Ruby code. When this fixture is loaded, the +size+ attribute of the three records will be set to 20/50, 20/2, and 20-69 respectively. The +brightest_on+ attribute will also be evaluated and formatted by Rails to be compatible with the database. - h5. Fixtures in Action Rails by default automatically loads all fixtures from the +test/fixtures+ folder for your unit and functional test. Loading involves three steps: @@ -377,12 +357,12 @@ There are a bunch of different types of assertions you can use. Here's the compl |_.Assertion |_.Purpose| |+assert( boolean, [msg] )+ |Ensures that the object/expression is true.| -|+assert_equal( obj1, obj2, [msg] )+ |Ensures that +obj1 == obj2+ is true.| -|+assert_not_equal( obj1, obj2, [msg] )+ |Ensures that +obj1 == obj2+ is false.| -|+assert_same( obj1, obj2, [msg] )+ |Ensures that +obj1.equal?(obj2)+ is true.| -|+assert_not_same( obj1, obj2, [msg] )+ |Ensures that +obj1.equal?(obj2)+ is false.| +|+assert_equal( expected, actual, [msg] )+ |Ensures that +expected == actual+ is true.| +|+assert_not_equal( expected, actual, [msg] )+ |Ensures that +expected != actual+ is true.| +|+assert_same( expected, actual, [msg] )+ |Ensures that +expected.equal?(actual)+ is true.| +|+assert_not_same( expected, actual, [msg] )+ |Ensures that +!expected.equal?(actual)+ is true.| |+assert_nil( obj, [msg] )+ |Ensures that +obj.nil?+ is true.| -|+assert_not_nil( obj, [msg] )+ |Ensures that +obj.nil?+ is false.| +|+assert_not_nil( obj, [msg] )+ |Ensures that +!obj.nil?+ is true.| |+assert_match( regexp, string, [msg] )+ |Ensures that a string matches the regular expression.| |+assert_no_match( regexp, string, [msg] )+ |Ensures that a string doesn't match the regular expression.| |+assert_in_delta( expecting, actual, delta, [msg] )+ |Ensures that the numbers +expecting+ and +actual+ are within +delta+ of each other.| @@ -526,7 +506,7 @@ You also have access to three instance variables in your functional tests: h4. Testing Templates and Layouts -If you want to make sure that the response rendered the correct template and layout, you can use the +assert_template+ +If you want to make sure that the response rendered the correct template and layout, you can use the +assert_template+ method: <ruby> diff --git a/guides/source/upgrading_ruby_on_rails.textile b/guides/source/upgrading_ruby_on_rails.textile index 4bf4751127..b750cc2ffb 100644 --- a/guides/source/upgrading_ruby_on_rails.textile +++ b/guides/source/upgrading_ruby_on_rails.textile @@ -42,6 +42,10 @@ h4(#active_record4_0). Active Record The <tt>delete</tt> method in collection associations can now receive <tt>Fixnum</tt> or <tt>String</tt> arguments as record ids, besides records, pretty much like the <tt>destroy</tt> method does. Previously it raised <tt>ActiveRecord::AssociationTypeMismatch</tt> for such arguments. From Rails 4.0 on <tt>delete</tt> automatically tries to find the records matching the given ids before deleting them. +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 <tt>serialized_attributes</tt> and <tt>_attr_readonly</tt> to class methods only. Now you shouldn't use instance methods, it's deprecated. You must change them, e.g. <tt>self.serialized_attributes</tt> to <tt>self.class.serialized_attributes</tt>. + h4(#active_model4_0). Active Model Rails 4.0 has changed how errors attach with the <tt>ActiveModel::Validations::ConfirmationValidator</tt>. Now when confirmation validations fail the error will be attached to <tt>:#{attribute}_confirmation</tt> instead of <tt>attribute</tt>. @@ -50,7 +54,21 @@ h4(#action_pack4_0). Action Pack Rails 4.0 changed how <tt>assert_generates</tt>, <tt>assert_recognizes</tt>, and <tt>assert_routing</tt> work. Now all these assertions raise <tt>Assertion</tt> instead of <tt>ActionController::RoutingError</tt>. -Rails 4.0 also changed the way unicode character routes are drawn. Now you can draw unicode character routes directly. If you already draw such routes, you must change them, e.g. <tt>get Rack::Utils.escape('こんにちは'), :controller => 'welcome', :action => 'index'</tt> to <tt>get 'こんにちは', :controller => 'welcome', :action => 'index'</tt>. +Rails 4.0 also changed the way unicode character routes are drawn. Now you can draw unicode character routes directly. If you already draw such routes, you must change them, for example: + +<ruby> +get Rack::Utils.escape('こんにちは'), :controller => 'welcome', :action => 'index' +</ruby> + +becomes + +<ruby> +get 'こんにちは', :controller => 'welcome', :action => 'index' +</ruby> + +h4(#active_support4_0). Active Support + +Rails 4.0 Removed the <tt>j</tt> alias for <tt>ERB::Util#json_escape</tt> since <tt>j</tt> is already used for <tt>ActionView::Helpers::JavaScriptHelper#escape_javascript</tt>. h4(#helpers_order). Helpers Loading Order diff --git a/guides/w3c_validator.rb b/guides/w3c_validator.rb index 5e340499c4..6ef3df45a9 100644 --- a/guides/w3c_validator.rb +++ b/guides/w3c_validator.rb @@ -5,9 +5,9 @@ # Guides are taken from the output directory, from where all .html files are # submitted to the validator. # -# This script is prepared to be launched from the railties directory as a rake task: +# This script is prepared to be launched from the guides directory as a rake task: # -# rake validate_guides +# rake guides:validate # # If nothing is specified, all files will be validated, but you can check just # some of them using this environment variable: @@ -17,12 +17,12 @@ # enough: # # # validates only association_basics.html -# ONLY=assoc rake validate_guides +# rake guides:validate ONLY=assoc # # Separate many using commas: # # # validates only association_basics.html and migrations.html -# ONLY=assoc,migrations rake validate_guides +# rake guides:validate ONLY=assoc,migrations # # --------------------------------------------------------------------------- @@ -38,7 +38,12 @@ module RailsGuides errors_on_guides = {} guides_to_validate.each do |f| - results = validator.validate_file(f) + begin + results = validator.validate_file(f) + rescue Exception => e + puts "\nCould not validate #{f} because of #{e}" + next + end if results.validity print "." @@ -53,15 +58,15 @@ module RailsGuides private def guides_to_validate - guides = Dir["./guides/output/*.html"] - guides.delete("./guides/output/layout.html") + guides = Dir["./output/*.html"] + guides.delete("./output/layout.html") ENV.key?('ONLY') ? select_only(guides) : guides end def select_only(guides) prefixes = ENV['ONLY'].split(",").map(&:strip) guides.select do |guide| - prefixes.any? {|p| guide.start_with?("./guides/output/#{p}")} + prefixes.any? {|p| guide.start_with?("./output/#{p}")} end end |