diff options
Diffstat (limited to 'guides/source')
31 files changed, 1489 insertions, 1110 deletions
diff --git a/guides/source/3_2_release_notes.textile b/guides/source/3_2_release_notes.textile index 0f8fea2bf6..3524ea6595 100644 --- a/guides/source/3_2_release_notes.textile +++ b/guides/source/3_2_release_notes.textile @@ -299,7 +299,7 @@ end h5(#actionview_deprecations). Deprecations -* Passing formats or handlers to render :template and friends like <tt>render :template => "foo.html.erb"</tt> is deprecated. Instead, you can provide :handlers and :formats directly as an options: <tt> render :template => "foo", :formats => [:html, :js], :handlers => :erb</tt>. +* Passing formats or handlers to render :template and friends like <tt>render :template => "foo.html.erb"</tt> is deprecated. Instead, you can provide :handlers and :formats directly as options: <tt> render :template => "foo", :formats => [:html, :js], :handlers => :erb</tt>. h4. Sprockets diff --git a/guides/source/action_controller_overview.textile b/guides/source/action_controller_overview.textile index 52d134ace5..cc3350819b 100644 --- a/guides/source/action_controller_overview.textile +++ b/guides/source/action_controller_overview.textile @@ -148,18 +148,19 @@ In this case, when a user opens the URL +/clients/active+, +params[:status]+ wil h4. +default_url_options+ -You can set global default parameters that will be used when generating URLs with +default_url_options+. To do this, define a method with that name in your controller: +You can set global default parameters for URL generation by defining a method called +default_url_options+ in your controller. Such a method must return a hash with the desired defaults, whose keys must be symbols: <ruby> class ApplicationController < ActionController::Base - # The options parameter is the hash passed in to 'url_for' - def default_url_options(options) + def default_url_options {:locale => I18n.locale} end end </ruby> -These options will be used as a starting-point when generating URLs, so it's possible they'll be overridden by +url_for+. Because this method is defined in the controller, you can define it on +ApplicationController+ so it would be used for all URL generation, or you could define it on only one controller for all URLs generated there. +These options will be used as a starting point when generating URLs, so it's possible they'll be overridden by the options passed in +url_for+ calls. + +If you define +default_url_options+ in +ApplicationController+, as in the example above, it would be used for all URL generation. The method can also be defined in one specific controller, in which case it only affects URLs generated there. h3. Session diff --git a/guides/source/action_mailer_basics.textile b/guides/source/action_mailer_basics.textile index c277f764e7..ebe774fbef 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.0. 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 diff --git a/guides/source/action_view_overview.textile b/guides/source/action_view_overview.textile index 42120e9bad..bde30ba21c 100644 --- a/guides/source/action_view_overview.textile +++ b/guides/source/action_view_overview.textile @@ -59,7 +59,6 @@ Now we'll create a simple "Hello World" application that uses the +titleize+ met *hello_world.rb:* <ruby> -require 'rubygems' require 'active_support/core_ext/string/inflections' require 'rack' @@ -94,7 +93,6 @@ Now we'll create the same "Hello World" application in Sinatra. *hello_world.rb:* <ruby> -require 'rubygems' require 'action_view' require 'sinatra' @@ -550,9 +548,9 @@ Register one or more JavaScript files to be included when symbol is passed to ja ActionView::Helpers::AssetTagHelper.register_javascript_expansion :monkey => ["head", "body", "tail"] javascript_include_tag :monkey # => - <script type="text/javascript" src="/javascripts/head.js"></script> - <script type="text/javascript" src="/javascripts/body.js"></script> - <script type="text/javascript" src="/javascripts/tail.js"></script> + <script src="/javascripts/head.js"></script> + <script src="/javascripts/body.js"></script> + <script src="/javascripts/tail.js"></script> </ruby> h5. register_stylesheet_expansion @@ -563,9 +561,9 @@ Register one or more stylesheet files to be included when symbol is passed to +s ActionView::Helpers::AssetTagHelper.register_stylesheet_expansion :monkey => ["head", "body", "tail"] stylesheet_link_tag :monkey # => - <link href="/stylesheets/head.css" media="screen" rel="stylesheet" type="text/css" /> - <link href="/stylesheets/body.css" media="screen" rel="stylesheet" type="text/css" /> - <link href="/stylesheets/tail.css" media="screen" rel="stylesheet" type="text/css" /> + <link href="/stylesheets/head.css" media="screen" rel="stylesheet" /> + <link href="/stylesheets/body.css" media="screen" rel="stylesheet" /> + <link href="/stylesheets/tail.css" media="screen" rel="stylesheet" /> </ruby> h5. auto_discovery_link_tag @@ -607,7 +605,7 @@ Returns an html script tag for each of the sources provided. You can pass in the <ruby> javascript_include_tag "common" # => - <script type="text/javascript" src="/javascripts/common.js"></script> + <script src="/javascripts/common.js"></script> </ruby> If the application does not use the asset pipeline, to include the jQuery JavaScript library in your application, pass +:defaults+ as the source. When using +:defaults+, if an +application.js+ file exists in your +public/javascripts+ directory, it will be included as well. @@ -626,7 +624,7 @@ You can also cache multiple JavaScript files into one file, which requires less <ruby> javascript_include_tag :all, :cache => true # => - <script type="text/javascript" src="/javascripts/all.js"></script> + <script src="/javascripts/all.js"></script> </ruby> h5. javascript_path @@ -651,7 +649,7 @@ Returns a stylesheet link tag for the sources specified as arguments. If you don <ruby> stylesheet_link_tag "application" # => - <link href="/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" /> + <link href="/stylesheets/application.css" media="screen" rel="stylesheet" /> </ruby> You can also include all styles in the stylesheet directory using :all as the source: @@ -664,7 +662,7 @@ You can also cache multiple stylesheets into one file, which requires less HTTP <ruby> stylesheet_link_tag :all, :cache => true - <link href="/stylesheets/all.css" media="screen" rel="stylesheet" type="text/css" /> + <link href="/stylesheets/all.css" media="screen" rel="stylesheet" /> </ruby> h5. stylesheet_path @@ -805,7 +803,7 @@ For example, let's say we have a standard application layout, but also a special <p>This is a special page.</p> <% content_for :special_script do %> - <script type="text/javascript">alert('Hello!')</script> + <script>alert('Hello!')</script> <% end %> </ruby> @@ -833,7 +831,7 @@ Reports the approximate distance in time between two Time or Date objects or int <ruby> distance_of_time_in_words(Time.now, Time.now + 15.seconds) # => less than a minute -distance_of_time_in_words(Time.now, Time.now + 15.seconds, true) # => less than 20 seconds +distance_of_time_in_words(Time.now, Time.now + 15.seconds, :include_seconds => true) # => less than 20 seconds </ruby> h5. select_date @@ -1501,7 +1499,7 @@ javascript_tag "alert('All is good')" </ruby> <html> -<script type="text/javascript"> +<script> //<![CDATA[ alert('All is good') //]]> diff --git a/guides/source/active_model_basics.textile b/guides/source/active_model_basics.textile index 98b3533000..d373f4ac85 100644 --- a/guides/source/active_model_basics.textile +++ b/guides/source/active_model_basics.textile @@ -20,7 +20,7 @@ class Person attribute_method_prefix 'reset_' attribute_method_suffix '_highest?' - define_attribute_methods ['age'] + define_attribute_methods 'age' attr_accessor :age @@ -95,12 +95,11 @@ 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 <ruby> -require 'rubygems' require 'active_model' class Person include ActiveModel::Dirty - define_attribute_methods [:first_name, :last_name] + define_attribute_methods :first_name, :last_name def first_name @first_name diff --git a/guides/source/active_record_querying.textile b/guides/source/active_record_querying.textile index 14d0ba9b28..294ef25b33 100644 --- a/guides/source/active_record_querying.textile +++ b/guides/source/active_record_querying.textile @@ -99,9 +99,28 @@ SELECT * FROM clients WHERE (clients.id = 10) LIMIT 1 <tt>Model.find(primary_key)</tt> will raise an +ActiveRecord::RecordNotFound+ exception if no matching record is found. +h5. +take+ + +<tt>Model.take</tt> retrieves a record without any implicit ordering. For example: + +<ruby> +client = Client.take +# => #<Client id: 1, first_name: "Lifo"> +</ruby> + +The SQL equivalent of the above is: + +<sql> +SELECT * FROM clients LIMIT 1 +</sql> + +<tt>Model.take</tt> returns +nil+ if no record is found and no exception will be raised. + +TIP: The retrieved record may vary depending on the database engine. + h5. +first+ -<tt>Model.first</tt> finds the first record matched by the supplied options, if any. For example: +<tt>Model.first</tt> finds the first record ordered by the primary key. For example: <ruby> client = Client.first @@ -111,14 +130,14 @@ client = Client.first The SQL equivalent of the above is: <sql> -SELECT * FROM clients LIMIT 1 +SELECT * FROM clients ORDER BY clients.id ASC LIMIT 1 </sql> -<tt>Model.first</tt> returns +nil+ if no matching record is found. No exception will be raised. +<tt>Model.first</tt> returns +nil+ if no matching record is found and no exception will be raised. h5. +last+ -<tt>Model.last</tt> finds the last record matched by the supplied options. For example: +<tt>Model.last</tt> finds the last record ordered by the primary key. For example: <ruby> client = Client.last @@ -131,7 +150,7 @@ The SQL equivalent of the above is: SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1 </sql> -<tt>Model.last</tt> returns +nil+ if no matching record is found. No exception will be raised. +<tt>Model.last</tt> returns +nil+ if no matching record is found and no exception will be raised. h5. +find_by+ @@ -148,12 +167,29 @@ Client.find_by first_name: 'Jon' It is equivalent to writing: <ruby> -Client.where(first_name: 'Lifo').first +Client.where(first_name: 'Lifo').take +</ruby> + +h5(#take_1). +take!+ + +<tt>Model.take!</tt> retrieves a record without any implicit ordering. For example: + +<ruby> +client = Client.take! +# => #<Client id: 1, first_name: "Lifo"> </ruby> +The SQL equivalent of the above is: + +<sql> +SELECT * FROM clients LIMIT 1 +</sql> + +<tt>Model.take!</tt> raises +ActiveRecord::RecordNotFound+ if no matching record is found. + h5(#first_1). +first!+ -<tt>Model.first!</tt> finds the first record. For example: +<tt>Model.first!</tt> finds the first record ordered by the primary key. For example: <ruby> client = Client.first! @@ -163,14 +199,14 @@ client = Client.first! The SQL equivalent of the above is: <sql> -SELECT * FROM clients LIMIT 1 +SELECT * FROM clients ORDER BY clients.id ASC LIMIT 1 </sql> -<tt>Model.first!</tt> raises +RecordNotFound+ if no matching record is found. +<tt>Model.first!</tt> raises +ActiveRecord::RecordNotFound+ if no matching record is found. h5(#last_1). +last!+ -<tt>Model.last!</tt> finds the last record. For example: +<tt>Model.last!</tt> finds the last record ordered by the primary key. For example: <ruby> client = Client.last! @@ -183,24 +219,24 @@ The SQL equivalent of the above is: SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1 </sql> -<tt>Model.last!</tt> raises +RecordNotFound+ if no matching record is found. +<tt>Model.last!</tt> raises +ActiveRecord::RecordNotFound+ if no matching record is found. h5(#find_by_1). +find_by!+ -<tt>Model.find_by!</tt> finds the first record matching some conditions. It raises +RecordNotFound+ if no matching record is found. For example: +<tt>Model.find_by!</tt> finds the first record matching some conditions. It raises +ActiveRecord::RecordNotFound+ if no matching record is found. For example: <ruby> Client.find_by! first_name: 'Lifo' # => #<Client id: 1, first_name: "Lifo"> Client.find_by! first_name: 'Jon' -# => RecordNotFound +# => ActiveRecord::RecordNotFound </ruby> It is equivalent to writing: <ruby> -Client.where(first_name: 'Lifo').first! +Client.where(first_name: 'Lifo').take! </ruby> h4. Retrieving Multiple Objects @@ -356,20 +392,6 @@ Client.where("created_at >= :start_date AND created_at <= :end_date", This makes for clearer readability if you have a large number of variable conditions. -h5(#array-range_conditions). Range Conditions - -If you're looking for a range inside of a table (for example, users created in a certain timeframe) you can use the conditions option coupled with the +IN+ SQL statement for this. If you had two dates coming in from a controller you could do something like this to look for a range: - -<ruby> -Client.where(:created_at => (params[:start_date].to_date)..(params[:end_date].to_date)) -</ruby> - -This query will generate something similar to the following SQL: - -<sql> - SELECT "clients".* FROM "clients" WHERE ("clients"."created_at" BETWEEN '2010-09-29' AND '2010-11-30') -</sql> - h4. Hash Conditions Active Record also allows you to pass in hash conditions which can increase the readability of your conditions syntax. With hash conditions, you pass in a hash with keys of the fields you want conditionalised and the values of how you want to conditionalise them: @@ -388,9 +410,9 @@ The field name can also be a string: Client.where('locked' => true) </ruby> -h5(#hash-range_conditions). Range Conditions +NOTE: The values cannot be symbols. For example, you cannot do +Client.where(:status => :active)+. -The good thing about this is that we can pass in a range for our fields without it generating a large query as shown in the preamble of this section. +h5(#hash-range_conditions). Range Conditions <ruby> Client.where(:created_at => (Time.now.midnight - 1.day)..Time.now.midnight) @@ -539,7 +561,9 @@ And this will give you a single +Order+ object for each date where there are ord The SQL that would be executed would be something like this: <sql> -SELECT date(created_at) as ordered_date, sum(price) as total_price FROM orders GROUP BY date(created_at) +SELECT date(created_at) as ordered_date, sum(price) as total_price +FROM orders +GROUP BY date(created_at) </sql> h3. Having @@ -555,7 +579,10 @@ Order.select("date(created_at) as ordered_date, sum(price) as total_price").grou The SQL that would be executed would be something like this: <sql> -SELECT date(created_at) as ordered_date, sum(price) as total_price FROM orders GROUP BY date(created_at) HAVING sum(price) > 100 +SELECT date(created_at) as ordered_date, sum(price) as total_price +FROM orders +GROUP BY date(created_at) +HAVING sum(price) > 100 </sql> This will return single order objects for each day, but only those that are ordered more than $100 in a day. @@ -695,7 +722,7 @@ Optimistic locking allows multiple users to access the same record for edits, an <strong>Optimistic locking column</strong> -In order to use optimistic locking, the table needs to have a column called +lock_version+. Each time the record is updated, Active Record increments the +lock_version+ column. If an update request is made with a lower value in the +lock_version+ field than is currently in the +lock_version+ column in the database, the update request will fail with an +ActiveRecord::StaleObjectError+. Example: +In order to use optimistic locking, the table needs to have a column called +lock_version+ of type integer. Each time the record is updated, Active Record increments the +lock_version+ column. If an update request is made with a lower value in the +lock_version+ field than is currently in the +lock_version+ column in the database, the update request will fail with an +ActiveRecord::StaleObjectError+. Example: <ruby> c1 = Client.find(1) @@ -829,7 +856,7 @@ SELECT categories.* FROM categories INNER JOIN posts ON posts.category_id = categories.id </sql> -Or, in English: "return a Category object for all categories with posts". Note that you will see duplicate categories if more than one post has the same category. If you want unique categories, you can use Category.joins(:post).select("distinct(categories.id)"). +Or, in English: "return a Category object for all categories with posts". Note that you will see duplicate categories if more than one post has the same category. If you want unique categories, you can use Category.joins(:posts).select("distinct(categories.id)"). h5. Joining Multiple Associations @@ -919,7 +946,7 @@ This code looks fine at the first sight. But the problem lies within the total n Active Record lets you specify in advance all the associations that are going to be loaded. This is possible by specifying the +includes+ method of the +Model.find+ call. With +includes+, Active Record ensures that all of the specified associations are loaded using the minimum possible number of queries. -Revisiting the above case, we could rewrite +Client.all+ to use eager load addresses: +Revisiting the above case, we could rewrite +Client.limit(10)+ to use eager load addresses: <ruby> clients = Client.includes(:address).limit(10) @@ -1004,7 +1031,7 @@ Scopes are also chainable within scopes: <ruby> class Post < ActiveRecord::Base scope :published, -> { where(:published => true) } - scope :published_and_commented, -> { published.and(self.arel_table[:comments_count].gt(0)) } + scope :published_and_commented, -> { published.where("comments_count > 0") } end </ruby> @@ -1233,6 +1260,24 @@ with Client.pluck(:id) </ruby> +h3. +ids+ + ++ids+ can be used to pluck all the IDs for the relation using the table's primary key. + +<ruby> +Person.ids +# SELECT id FROM people +</ruby> + +<ruby> +class Person < ActiveRecord::Base + self.primary_key = "person_id" +end + +Person.ids +# SELECT person_id FROM people +</ruby> + h3. Existence of Objects If you simply want to check for the existence of the object there's a method called +exists?+. This method will query the database using the same query as +find+, but instead of returning an object or collection of objects it will return either +true+ or +false+. diff --git a/guides/source/active_record_validations_callbacks.textile b/guides/source/active_record_validations_callbacks.textile index 88c4481e5e..f49d91fd3c 100644 --- a/guides/source/active_record_validations_callbacks.textile +++ b/guides/source/active_record_validations_callbacks.textile @@ -1064,6 +1064,7 @@ Additionally, the +after_find+ callback is triggered by the following finder met * +find_all_by_<em>attribute</em>+ * +find_by_<em>attribute</em>+ * +find_by_<em>attribute</em>!+ +* +find_by_sql+ * +last+ The +after_initialize+ callback is triggered every time a new object of the class is initialized. @@ -1076,7 +1077,6 @@ Just as with validations, it is also possible to skip callbacks. These methods s * +decrement_counter+ * +delete+ * +delete_all+ -* +find_by_sql+ * +increment+ * +increment_counter+ * +toggle+ diff --git a/guides/source/active_support_core_extensions.textile b/guides/source/active_support_core_extensions.textile index 5d0a3f82e8..6443255f5d 100644 --- a/guides/source/active_support_core_extensions.textile +++ b/guides/source/active_support_core_extensions.textile @@ -154,6 +154,51 @@ WARNING. Any class can disallow duplication removing +dup+ and +clone+ or raisin NOTE: Defined in +active_support/core_ext/object/duplicable.rb+. +h4. +deep_dup+ + +The +deep_dup+ method returns deep copy of given object. Normally, when you +dup+ an object that contains other objects, ruby does not +dup+ them. If you have array with a string, for example, it will look like this: + +<ruby> +array = ['string'] +duplicate = array.dup + +duplicate.push 'another-string' + +# object was duplicated, element added only to duplicate +array #=> ['string'] +duplicate #=> ['string', 'another-string'] + +duplicate.first.gsub!('string', 'foo') + +# first element was not duplicated, it will be changed for both arrays +array #=> ['foo'] +duplicate #=> ['foo', 'another-string'] +</ruby> + +As you can see, after duplicating +Array+ instance, we got another object, therefore we can modify it and the original object will stay unchanged. This is not true for array's elements, however. Since +dup+ does not make deep copy, the string inside array is still the same object. + +If you need a deep copy of an object, you should use +deep_dup+ in such situation: + +<ruby> +array = ['string'] +duplicate = array.deep_dup + +duplicate.first.gsub!('string', 'foo') + +array #=> ['string'] +duplicate #=> ['foo'] +</ruby> + +If object is not duplicable +deep_dup+ will just return this object: + +<ruby> +number = 1 +dup = number.deep_dup +number.object_id == dup.object_id # => true +</ruby> + +NOTE: Defined in +active_support/core_ext/object/deep_dup.rb+. + h4. +try+ Sometimes you want to call a method provided the receiver object is not +nil+, which is something you usually check first. +try+ is like +Object#send+ except that it returns +nil+ if sent to +nil+. @@ -184,7 +229,7 @@ You can evaluate code in the context of any object's singleton class using +clas <ruby> class Proc def bind(object) - block, time = self, Time.now + block, time = self, Time.current object.class_eval do method_name = "__bind_#{time.to_i}_#{time.usec}" define_method(method_name, &block) @@ -1034,49 +1079,6 @@ A model may find it useful to set +:instance_accessor+ to +false+ as a way to pr NOTE: Defined in +active_support/core_ext/class/attribute_accessors.rb+. -h4. Class Inheritable Attributes - -WARNING: Class Inheritable Attributes are deprecated. It's recommended that you use +Class#class_attribute+ instead. - -Class variables are shared down the inheritance tree. Class instance variables are not shared, but they are not inherited either. The macros +class_inheritable_reader+, +class_inheritable_writer+, and +class_inheritable_accessor+ provide accessors for class-level data which is inherited but not shared with children: - -<ruby> -module ActionController - class Base - # FIXME: REVISE/SIMPLIFY THIS COMMENT. - # The value of allow_forgery_protection is inherited, - # but its value in a particular class does not affect - # the value in the rest of the controllers hierarchy. - class_inheritable_accessor :allow_forgery_protection - end -end -</ruby> - -They accomplish this with class instance variables and cloning on subclassing, there are no class variables involved. Cloning is performed with +dup+ as long as the value is duplicable. - -There are some variants specialised in arrays and hashes: - -<ruby> -class_inheritable_array -class_inheritable_hash -</ruby> - -Those writers take any inherited array or hash into account and extend them rather than overwrite them. - -As with vanilla class attribute accessors these macros create convenience instance methods for reading and writing. The generation of the writer instance method can be prevented setting +:instance_writer+ to +false+ (not any false value, but exactly +false+): - -<ruby> -module ActiveRecord - class Base - class_inheritable_accessor :default_scoping, :instance_writer => false - end -end -</ruby> - -Since values are copied when a subclass is defined, if the base class changes the attribute after that, the subclass does not see the new value. That's the point. - -NOTE: Defined in +active_support/core_ext/class/inheritable_attributes.rb+. - h4. Subclasses & Descendants h5. +subclasses+ @@ -1131,7 +1133,7 @@ h4. Output Safety h5. Motivation -Inserting data into HTML templates needs extra care. For example you can't just interpolate +@review.title+ verbatim into an HTML page. On one hand if the review title is "Flanagan & Matz rules!" the output won't be well-formed because an ampersand has to be escaped as "&amp;". On the other hand, depending on the application that may be a big security hole because users can inject malicious HTML setting a hand-crafted review title. Check out the "section about cross-site scripting in the Security guide":security.html#cross-site-scripting-xss for further information about the risks. +Inserting data into HTML templates needs extra care. For example, you can't just interpolate +@review.title+ verbatim into an HTML page. For one thing, if the review title is "Flanagan & Matz rules!" the output won't be well-formed because an ampersand has to be escaped as "&amp;". What's more, depending on the application, that may be a big security hole because users can inject malicious HTML setting a hand-crafted review title. Check out the "section about cross-site scripting in the Security guide":security.html#cross-site-scripting-xss for further information about the risks. h5. Safe Strings @@ -1255,9 +1257,14 @@ Pass a +:separator+ to truncate the string at a natural break: # => "Oh dear! Oh..." </ruby> -In the above example "dear" gets cut first, but then +:separator+ prevents it. +The option +:separator+ can be a regexp: -WARNING: The option +:separator+ can't be a regexp. +<ruby> +"Oh dear! Oh dear! I shall be late!".truncate(18, :separator => /\s/) +# => "Oh dear! Oh..." +</ruby> + +In above examples "dear" gets cut first, but then +:separator+ prevents it. NOTE: Defined in +active_support/core_ext/string/filters.rb+. @@ -1270,20 +1277,6 @@ The <tt>inquiry</tt> method converts a string into a +StringInquirer+ object mak "active".inquiry.inactive? # => false </ruby> -h4. Key-based Interpolation - -In Ruby 1.9 the <tt>%</tt> string operator supports key-based interpolation, both formatted and unformatted: - -<ruby> -"Total is %<total>.02f" % {:total => 43.1} # => Total is 43.10 -"I say %{foo}" % {:foo => "wadus"} # => "I say wadus" -"I say %{woo}" % {:foo => "wadus"} # => KeyError -</ruby> - -Active Support adds that functionality to <tt>%</tt> in previous versions of Ruby. - -NOTE: Defined in +active_support/core_ext/string/interpolation.rb+. - h4. +starts_with?+ and +ends_with?+ Active Support defines 3rd person aliases of +String#start_with?+ and +String#end_with?+: @@ -1330,7 +1323,7 @@ Returns the character of the string at position +position+: "hello".at(0) # => "h" "hello".at(4) # => "o" "hello".at(-1) # => "o" -"hello".at(10) # => ERROR if < 1.9, nil in 1.9 +"hello".at(10) # => nil </ruby> NOTE: Defined in +active_support/core_ext/string/access.rb+. @@ -1810,6 +1803,43 @@ Singular forms are aliased so you are able to say: NOTE: Defined in +active_support/core_ext/numeric/bytes.rb+. +h4. Time + +Enables the use of time calculations and declarations, like @45.minutes <plus> 2.hours <plus> 4.years@. + +These methods use Time#advance for precise date calculations when using from_now, ago, etc. +as well as adding or subtracting their results from a Time object. For example: + +<ruby> +# equivalent to Time.current.advance(:months => 1) +1.month.from_now + +# equivalent to Time.current.advance(:years => 2) +2.years.from_now + +# equivalent to Time.current.advance(:months => 4, :years => 5) +(4.months + 5.years).from_now +</ruby> + +While these methods provide precise calculation when used as in the examples above, care +should be taken to note that this is not true if the result of `months', `years', etc is +converted before use: + +<ruby> +# equivalent to 30.days.to_i.from_now +1.month.to_i.from_now + +# equivalent to 365.25.days.to_f.from_now +1.year.to_f.from_now +</ruby> + +In such cases, Ruby's core +Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and +Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision +date and time arithmetic. + +NOTE: Defined in +active_support/core_ext/numeric/time.rb+. + h3. Extensions to +Integer+ h4. +multiple_of?+ @@ -2103,20 +2133,20 @@ To do so it sends +to_xml+ to every item in turn, and collects the results under By default, the name of the root element is the underscorized and dasherized plural of the name of the class of the first item, provided the rest of elements belong to that type (checked with <tt>is_a?</tt>) and they are not hashes. In the example above that's "contributors". -If there's any element that does not belong to the type of the first one the root node becomes "records": +If there's any element that does not belong to the type of the first one the root node becomes "objects": <ruby> [Contributor.first, Commit.first].to_xml # => # <?xml version="1.0" encoding="UTF-8"?> -# <records type="array"> -# <record> +# <objects type="array"> +# <object> # <id type="integer">4583</id> # <name>Aaron Batalion</name> # <rank type="integer">53</rank> # <url-id>aaron-batalion</url-id> -# </record> -# <record> +# </object> +# <object> # <author>Joshua Peek</author> # <authored-timestamp type="datetime">2009-09-02T16:44:36Z</authored-timestamp> # <branch>origin/master</branch> @@ -2127,30 +2157,30 @@ If there's any element that does not belong to the type of the first one the roo # <imported-from-svn type="boolean">false</imported-from-svn> # <message>Kill AMo observing wrap_with_notifications since ARes was only using it</message> # <sha1>723a47bfb3708f968821bc969a9a3fc873a3ed58</sha1> -# </record> -# </records> +# </object> +# </objects> </ruby> -If the receiver is an array of hashes the root element is by default also "records": +If the receiver is an array of hashes the root element is by default also "objects": <ruby> [{:a => 1, :b => 2}, {:c => 3}].to_xml # => # <?xml version="1.0" encoding="UTF-8"?> -# <records type="array"> -# <record> +# <objects type="array"> +# <object> # <b type="integer">2</b> # <a type="integer">1</a> -# </record> -# <record> +# </object> +# <object> # <c type="integer">3</c> -# </record> -# </records> +# </object> +# </objects> </ruby> WARNING. If the collection is empty the root element is by default "nil-classes". That's a gotcha, for example the root element of the list of contributors above would not be "contributors" if the collection was empty, but "nil-classes". You may use the <tt>:root</tt> option to ensure a consistent root element. -The name of children nodes is by default the name of the root node singularized. In the examples above we've seen "contributor" and "record". The option <tt>:children</tt> allows you to set these node names. +The name of children nodes is by default the name of the root node singularized. In the examples above we've seen "contributor" and "object". The option <tt>:children</tt> allows you to set these node names. The default XML builder is a fresh instance of <tt>Builder::XmlMarkup</tt>. You can configure your own builder via the <tt>:builder</tt> option. The method also accepts options like <tt>:dasherize</tt> and friends, they are forwarded to the builder: @@ -2217,6 +2247,19 @@ Thus, in this case the behavior is different for +nil+, and the differences with NOTE: Defined in +active_support/core_ext/array/wrap.rb+. +h4. Duplicating + +The method +Array.deep_dup+ duplicates itself and all objects inside recursively with ActiveSupport method +Object#deep_dup+. It works like +Array#map+ with sending +deep_dup+ method to each object inside. + +<ruby> +array = [1, [2, 3]] +dup = array.deep_dup +dup[1][2] = 4 +array[1][2] == nil # => true +</ruby> + +NOTE: Defined in +active_support/core_ext/array/deep_dup.rb+. + h4. Grouping h5. +in_groups_of(number, fill_with = nil)+ @@ -2423,6 +2466,23 @@ The method +deep_merge!+ performs a deep merge in place. NOTE: Defined in +active_support/core_ext/hash/deep_merge.rb+. +h4. Deep duplicating + +The method +Hash.deep_dup+ duplicates itself and all keys and values inside recursively with ActiveSupport method +Object#deep_dup+. It works like +Enumerator#each_with_object+ with sending +deep_dup+ method to each pair inside. + +<ruby> +hash = { :a => 1, :b => { :c => 2, :d => [3, 4] } } + +dup = hash.deep_dup +dup[:b][:e] = 5 +dup[:b][:d] << 5 + +hash[:b][:e] == nil # => true +hash[:b][:d] == [3, 4] # => true +</ruby> + +NOTE: Defined in +active_support/core_ext/hash/deep_dup.rb+. + h4. Diffing The method +diff+ returns a hash that represents a diff of the receiver and the argument with the following logic: @@ -2693,36 +2753,27 @@ As the example depicts, the +:db+ format generates a +BETWEEN+ SQL clause. That NOTE: Defined in +active_support/core_ext/range/conversions.rb+. -h4. +step+ - -Active Support extends the method +Range#step+ so that it can be invoked without a block: - -<ruby> -(1..10).step(2) # => [1, 3, 5, 7, 9] -</ruby> - -As the example shows, in that case the method returns an array with the corresponding elements. - -NOTE: Defined in +active_support/core_ext/range/blockless_step.rb+. - h4. +include?+ -The method +Range#include?+ says whether some value falls between the ends of a given instance: +The methods +Range#include?+ and +Range#===+ say whether some value falls between the ends of a given instance: <ruby> (2..3).include?(Math::E) # => true </ruby> -Active Support extends this method so that the argument may be another range in turn. In that case we test whether the ends of the argument range belong to the receiver themselves: +Active Support extends these methods so that the argument may be another range in turn. In that case we test whether the ends of the argument range belong to the receiver themselves: <ruby> (1..10).include?(3..7) # => true (1..10).include?(0..7) # => false (1..10).include?(3..11) # => false (1...9).include?(3..9) # => false -</ruby> -WARNING: The original +Range#include?+ is still the one aliased to +Range#===+. +(1..10) === (3..7) # => true +(1..10) === (0..7) # => false +(1..10) === (3..11) # => false +(1...9) === (3..9) # => false +</ruby> NOTE: Defined in +active_support/core_ext/range/include_range.rb+. @@ -3052,18 +3103,38 @@ The method +beginning_of_day+ returns a timestamp at the beginning of the day (0 <ruby> date = Date.new(2010, 6, 7) -date.beginning_of_day # => Sun Jun 07 00:00:00 +0200 2010 +date.beginning_of_day # => Mon Jun 07 00:00:00 +0200 2010 </ruby> The method +end_of_day+ returns a timestamp at the end of the day (23:59:59): <ruby> date = Date.new(2010, 6, 7) -date.end_of_day # => Sun Jun 06 23:59:59 +0200 2010 +date.end_of_day # => Mon Jun 07 23:59:59 +0200 2010 </ruby> +beginning_of_day+ is aliased to +at_beginning_of_day+, +midnight+, +at_midnight+. +h6. +beginning_of_hour+, +end_of_hour+ + +The method +beginning_of_hour+ returns a timestamp at the beginning of the hour (hh:00:00): + +<ruby> +date = DateTime.new(2010, 6, 7, 19, 55, 25) +date.beginning_of_hour # => Mon Jun 07 19:00:00 +0200 2010 +</ruby> + +The method +end_of_hour+ returns a timestamp at the end of the hour (hh:59:59): + +<ruby> +date = DateTime.new(2010, 6, 7, 19, 55, 25) +date.end_of_hour # => Mon Jun 07 19:59:59 +0200 2010 +</ruby> + ++beginning_of_hour+ is aliased to +at_beginning_of_hour+. + +INFO: +beginning_of_hour+ and +end_of_hour+ are implemented for +Time+ and +DateTime+ but *not* +Date+ as it does not make sense to request the beginning or end of an hour on a +Date+ instance. + h6. +ago+, +since+ The method +ago+ receives a number of seconds as argument and returns a timestamp those many seconds ago from midnight: @@ -3131,6 +3202,13 @@ since (in) On the other hand, +advance+ and +change+ are also defined and support more options, they are documented below. +The following methods are only implemented in +active_support/core_ext/date_time/calculations.rb+ as they only make sense when used with a +DateTime+ instance: + +<ruby> +beginning_of_hour (at_beginning_of_hour) +end_of_hour +</ruby> + h5. Named Datetimes h6. +DateTime.current+ @@ -3273,6 +3351,8 @@ ago since (in) beginning_of_day (midnight, at_midnight, at_beginning_of_day) end_of_day +beginning_of_hour (at_beginning_of_hour) +end_of_hour beginning_of_week (at_beginning_of_week) end_of_week (at_end_of_week) monday diff --git a/guides/source/ajax_on_rails.textile b/guides/source/ajax_on_rails.textile index cda9c64460..bfd007490a 100644 --- a/guides/source/ajax_on_rails.textile +++ b/guides/source/ajax_on_rails.textile @@ -78,7 +78,7 @@ will produce <ruby> button_to('Destroy', 'http://www.example.com', :confirm => 'Are you sure?', - :method => "delete", :remote => true, :disable_with => 'loading...') + :method => "delete", :remote => true, 'data-disable-with' => 'loading...') </ruby> will produce @@ -87,7 +87,7 @@ will produce <form class='button_to' method='post' action='http://www.example.com' data-remote='true'> <div> <input name='_method' value='delete' type='hidden' /> - <input value='Destroy' type='submit' disable_with='loading...' data-confirm='Are you sure?' /> + <input value='Destroy' type='submit' data-disable-with='loading...' data-confirm='Are you sure?' /> </div> </form> </html> diff --git a/guides/source/asset_pipeline.textile b/guides/source/asset_pipeline.textile index a1b7a42d66..105efe229e 100644 --- a/guides/source/asset_pipeline.textile +++ b/guides/source/asset_pipeline.textile @@ -204,6 +204,8 @@ Images can also be organized into subdirectories if required, and they can be ac <%= image_tag "icons/rails.png" %> </erb> +WARNING: If you're precompiling your assets (see "In Production":#in-production below), linking to an asset that does not exist will raise an exception in the calling page. This includes linking to a blank string. As such, be careful using <tt>image_tag</tt> and the other helpers with user-supplied data. + h5. CSS and ERB The asset pipeline automatically evaluates ERB. This means that if you add an +erb+ extension to a CSS asset (for example, +application.css.erb+), then helpers like +asset_path+ are available in your CSS rules: @@ -328,9 +330,9 @@ This manifest +app/assets/javascripts/application.js+: would generate this HTML: <html> -<script src="/assets/core.js?body=1" type="text/javascript"></script> -<script src="/assets/projects.js?body=1" type="text/javascript"></script> -<script src="/assets/tickets.js?body=1" type="text/javascript"></script> +<script src="/assets/core.js?body=1"></script> +<script src="/assets/projects.js?body=1"></script> +<script src="/assets/tickets.js?body=1"></script> </html> The +body+ param is required by Sprockets. @@ -346,7 +348,7 @@ config.assets.debug = false When debug mode is off, Sprockets concatenates and runs the necessary preprocessors on all files. With debug mode turned off the manifest above would generate instead: <html> -<script src="/assets/application.js" type="text/javascript"></script> +<script src="/assets/application.js"></script> </html> Assets are compiled and cached on the first request after the server is started. Sprockets sets a +must-revalidate+ Cache-Control HTTP header to reduce request overhead on subsequent requests -- on these the browser gets a 304 (Not Modified) response. @@ -380,8 +382,8 @@ For example this: generates something like this: <html> -<script src="/assets/application-908e25f4bf641868d8683022a5b62f54.js" type="text/javascript"></script> -<link href="/assets/application-4dd5b109ee3439da54f5bdfd78a80473.css" media="screen" rel="stylesheet" type="text/css" /> +<script src="/assets/application-908e25f4bf641868d8683022a5b62f54.js"></script> +<link href="/assets/application-4dd5b109ee3439da54f5bdfd78a80473.css" media="screen" rel="stylesheet" /> </html> The fingerprinting behavior is controlled by the setting of +config.assets.digest+ setting in Rails (which defaults to +true+ for production and +false+ for everything else). @@ -394,7 +396,7 @@ Rails comes bundled with a rake task to compile the asset manifests and other fi Compiled assets are written to the location specified in +config.assets.prefix+. By default, this is the +public/assets+ directory. -You can call this task on the server during deployment to create compiled versions of your assets directly on the server. If you do not have write access to your production file system, you can call this task locally and then deploy the compiled assets. +You can call this task on the server during deployment to create compiled versions of your assets directly on the server. See the next section for information on compiling locally. The rake task is: @@ -409,9 +411,9 @@ cannot see application objects or methods. *Heroku requires this to be false.* WARNING: If you set +config.assets.initialize_on_precompile+ to false, be sure to test +rake assets:precompile+ locally before deploying. It may expose bugs where your assets reference application objects or methods, since those are still -in scope in development mode regardless of the value of this flag. Changing this flag also effects +in scope in development mode regardless of the value of this flag. Changing this flag also affects engines. Engines can define assets for precompilation as well. Since the complete environment is not loaded, -engines (or other gems) will not be loaded which can cause missing assets. +engines (or other gems) will not be loaded, which can cause missing assets. Capistrano (v2.8.0 and above) includes a recipe to handle this in deployment. Add the following line to +Capfile+: @@ -514,6 +516,41 @@ If you're compiling nginx with Phusion Passenger you'll need to pass that option A robust configuration for Apache is possible but tricky; please Google around. (Or help update this Guide if you have a good example configuration for Apache.) +h4. Local Precompilation + +There are several reasons why you might want to precompile your assets locally. Among them are: + +* You may not have write access to your production file system. +* You may be deploying to more than one server, and want to avoid the duplication of work. +* You may be doing frequent deploys that do not include asset changes. + +Local compilation allows you to commit the compiled files into source control, and deploy as normal. + +There are two caveats: + +* You must not run the Capistrano deployment task that precompiles assets. +* You must change the following two application configuration settings. + +In <tt>config/environments/development.rb</tt>, place the following line: + +<erb> +config.assets.prefix = "/dev-assets" +</erb> + +You will also need this in application.rb: + +<erb> +config.assets.initialize_on_precompile = false +</erb> + +The +prefix+ change makes Rails use a different URL for serving assets in development mode, and pass all requests to Sprockets. The prefix is still set to +/assets+ in the production environment. Without this change, the application would serve the precompiled assets from +public/assets+ in development, and you would not see any local changes until you compile assets again. + +The +initialize_on_precompile+ change tells the precompile task to run without invoking Rails. This is because the precompile task runs in production mode by default, and will attempt to connect to your specified production database. Please note that you cannot have code in pipeline files that relies on Rails resources (such as the database) when compiling locally with this option. + +You will also need to ensure that any compressors or minifiers are available on your development system. + +In practice, this will allow you to precompile locally, have those files in your working tree, and commit those files to source control when needed. Development mode will work as expected. + h4. Live Compilation In some circumstances you may wish to use live compilation. In this mode all requests for assets in the pipeline are handled by Sprockets directly. @@ -673,7 +710,7 @@ config.assets.compile = false # Generate digests for assets URLs. config.assets.digest = true -# Defaults to Rails.root.join("public/assets") +# Defaults to nil and saved in location specified by config.assets.prefix # config.assets.manifest = YOUR_PATH # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) diff --git a/guides/source/caching_with_rails.textile b/guides/source/caching_with_rails.textile index 0e811a2527..34a100cd3a 100644 --- a/guides/source/caching_with_rails.textile +++ b/guides/source/caching_with_rails.textile @@ -92,7 +92,7 @@ INFO: Page caching runs in an after filter. Thus, invalid requests won't generat h4. Action Caching -One of the issues with Page Caching is that you cannot use it for pages that require to restrict access somehow. This is where Action Caching comes in. Action Caching works like Page Caching except for the fact that the incoming web request does go from the webserver to the Rails stack and Action Pack so that before filters can be run on it before the cache is served. This allows authentication and other restriction to be run while still serving the result of the output from a cached copy. +Page Caching cannot be used for actions that have before filters - for example, pages that require authentication. This is where Action Caching comes in. Action Caching works like Page Caching except the incoming web request hits the Rails stack so that before filters can be run on it before the cache is served. This allows authentication and other restrictions to be run while still serving the result of the output from a cached copy. Clearing the cache works in a similar way to Page Caching, except you use +expire_action+ instead of +expire_page+. @@ -157,7 +157,7 @@ and you can expire it using the +expire_fragment+ method, like so: expire_fragment(:controller => 'products', :action => 'recent', :action_suffix => 'all_products') </ruby> -If you don't want the cache block to bind to the action that called it, You can also use globally keyed fragments by calling the +cache+ method with a key, like so: +If you don't want the cache block to bind to the action that called it, you can also use globally keyed fragments by calling the +cache+ method with a key: <ruby> <% cache('all_available_products') do %> @@ -229,6 +229,42 @@ class ProductsController < ActionController end </ruby> +Sometimes it is necessary to disambiguate the controller when you call +expire_action+, such as when there are two identically named controllers in separate namespaces: + +<ruby> +class ProductsController < ActionController + caches_action :index + + def index + @products = Product.all + end +end + +module Admin + class ProductsController < ActionController + cache_sweeper :product_sweeper + + def new + @product = Product.new + end + + def create + @product = Product.create(params[:product]) + end + end +end + +class ProductSweeper < ActionController::Caching::Sweeper + observe Product + + def after_create(product) + expire_action(:controller => '/products', :action => 'index') + end +end +</ruby> + +Note the use of '/products' here rather than 'products'. If you wanted to expire an action cache for the +Admin::ProductsController+, you would use 'admin/products' instead. + h4. SQL Caching Query caching is a Rails feature that caches the result set returned by each query so that if Rails encounters the same query again for that request, it will use the cached result set as opposed to running the query against the database again. diff --git a/guides/source/command_line.textile b/guides/source/command_line.textile index 858ce47db1..b656a0857a 100644 --- a/guides/source/command_line.textile +++ b/guides/source/command_line.textile @@ -12,7 +12,7 @@ endprologue. NOTE: This tutorial assumes you have basic Rails knowledge from reading the "Getting Started with Rails Guide":getting_started.html. -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.2. Some of the code shown here will not work in earlier versions of Rails. h3. Command Line Basics @@ -31,7 +31,7 @@ h4. +rails new+ The first thing we'll want to do is create a new Rails application by running the +rails new+ command after installing Rails. -WARNING: You can install the rails gem by typing +gem install rails+, if you don't have it already. Follow the instructions in the "Rails 3 Release Notes":/3_0_release_notes.html +TIP: You can install the rails gem by typing +gem install rails+, if you don't have it already. <shell> $ rails new commandsapp @@ -185,8 +185,6 @@ $ rails server => Booting WEBrick... </shell> -WARNING: Make sure that you do not have any "tilde backup" files in +app/views/(controller)+, or else WEBrick will _not_ show the expected output. This seems to be a *bug* in Rails 2.3.0. - The URL will be "http://localhost:3000/greetings/hello":http://localhost:3000/greetings/hello. INFO: With a normal, plain-old Rails application, your URLs will generally follow the pattern of http://(host)/(controller)/(action), and a URL like http://(host)/(controller) will hit the *index* action of that controller. @@ -446,6 +444,18 @@ app/model/post.rb: NOTE. When using specific annotations and custom annotations, the annotation name (FIXME, BUG etc) is not displayed in the output lines. +By default, +rake notes+ will look in the +app+, +config+, +lib+, +script+ and +test+ directories. If you would like to search other directories, you can provide them as a comma separated list in an environment variable +SOURCE_ANNOTATION_DIRECTORIES+. + +<shell> +$ export SOURCE_ANNOTATION_DIRECTORIES='rspec,vendor' +$ rake notes +(in /home/foobar/commandsapp) +app/model/user.rb: + * [ 35] [FIXME] User should have a subscription at this point +rspec/model/user_spec.rb: + * [122] [TODO] Verify the user that has a subscription works +</shell> + h4. +routes+ +rake routes+ will list all of your defined routes, which is useful for tracking down routing problems in your app, or giving you a good overview of the URLs in an app you're trying to get familiar with. diff --git a/guides/source/configuring.textile b/guides/source/configuring.textile index 717654d5d8..f114075cae 100644 --- a/guides/source/configuring.textile +++ b/guides/source/configuring.textile @@ -70,12 +70,23 @@ NOTE. The +config.asset_path+ configuration is ignored if the asset pipeline is * +config.action_view.cache_template_loading+ controls whether or not templates should be reloaded on each request. Defaults to whatever is set for +config.cache_classes+. -* +config.cache_store+ configures which cache store to use for Rails caching. Options include one of the symbols +:memory_store+, +:file_store+, +:mem_cache_store+, or an object that implements the cache API. Defaults to +:file_store+ if the directory +tmp/cache+ exists, and to +:memory_store+ otherwise. +* +config.cache_store+ configures which cache store to use for Rails caching. Options include one of the symbols +:memory_store+, +:file_store+, +:mem_cache_store+, +:null_store+, or an object that implements the cache API. Defaults to +:file_store+ if the directory +tmp/cache+ exists, and to +:memory_store+ otherwise. * +config.colorize_logging+ specifies whether or not to use ANSI color codes when logging information. Defaults to true. * +config.consider_all_requests_local+ is a flag. If true then any error will cause detailed debugging information to be dumped in the HTTP response, and the +Rails::Info+ controller will show the application runtime context in +/rails/info/properties+. True by default in development and test environments, and false in production mode. For finer-grained control, set this to false and implement +local_request?+ in controllers to specify which requests should provide debugging information on errors. +* +config.console+ allows you to set class that will be used as console you run +rails console+. It's best to run it in +console+ block: + +<ruby> +console do + # this block is called only when running console, + # so we can safely require pry here + require "pry" + config.console = Pry +end +</ruby> + * +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_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. @@ -100,6 +111,10 @@ NOTE. The +config.asset_path+ configuration is ignored if the asset pipeline is * +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+. + * +config.reload_classes_only_on_change+ enables or disables reloading of classes only when tracked files change. By default tracks everything on autoload paths and is set to true. If +config.cache_classes+ is true, this option is ignored. * +config.secret_token+ used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get +config.secret_token+ initialized to a random key in +config/initializers/secret_token.rb+. @@ -122,17 +137,6 @@ WARNING: Threadsafe operation is incompatible with the normal workings of develo * +config.whiny_nils+ enables or disables warnings when a certain set of methods are invoked on +nil+ and it does not respond to them. Defaults to true in development and test environments. -* +config.console+ allows you to set class that will be used as console you run +rails console+. It's best to run it in +console+ block: - -<ruby> -console do - # this block is called only when running console, - # so we can safely require pry here - require "pry" - config.console = Pry -end -</ruby> - h4. Configuring Assets Rails 3.1, by default, is set up to use the +sprockets+ gem to manage assets within an application. This gem concatenates and compresses assets in order to make serving them much less painful. @@ -182,13 +186,13 @@ 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 ran. Defaults to +true+. +* +javascripts+ turns on the hook for javascripts 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+. * +resource_controller+ defines which generator to use for generating a controller when using +rails generate resource+. Defaults to +:controller+. * +scaffold_controller+ different from +resource_controller+, defines which generator to use for generating a _scaffolded_ controller when using +rails generate scaffold+. Defaults to +:scaffold_controller+. -* +stylesheets+ turns on the hook for stylesheets in generators. Used in Rails for when the +scaffold+ generator is ran, but this hook can be used in other generates as well. Defaults to +true+. +* +stylesheets+ turns on the hook for stylesheets in generators. Used in Rails for when the +scaffold+ generator is run, but this hook can be used in other generates as well. Defaults to +true+. * +stylesheet_engine+ configures the stylesheet engine (for eg. sass) to be used when generating assets. Defaults to +:css+. * +test_framework+ defines which test framework to use. Defaults to +false+ and will use Test::Unit by default. * +template_engine+ defines which template engine to use, such as ERB or Haml. Defaults to +:erb+. @@ -248,14 +252,6 @@ They can also be removed from the stack completely: config.middleware.delete ActionDispatch::BestStandardsSupport </ruby> -In addition to these methods to handle the stack, if your application is going to be used as an API endpoint only, the middleware stack can be configured like this: - -<ruby> -config.middleware.http_only! -</ruby> - -By doing this, Rails will create a smaller middleware stack, by not adding some middlewares that are usually useful for browser access only, such as Cookies, Session and Flash, BestStandardsSupport, and MethodOverride. You can always add any of them later manually if you want. Refer to the "API App docs":api_app.html for more info on how to setup your application for API only apps. - h4. Configuring i18n * +config.i18n.default_locale+ sets the default locale of an application used for i18n. Defaults to +:en+. @@ -362,7 +358,7 @@ h4. Configuring Action View Proc.new { |html_tag, instance| %Q(<div class="field_with_errors">#{html_tag}</div>).html_safe } </ruby> -* +config.action_view.default_form_builder+ tells Rails which form builder to use by default. The default is +ActionView::Helpers::FormBuilder+. +* +config.action_view.default_form_builder+ tells Rails which form builder to use by default. The default is +ActionView::Helpers::FormBuilder+. If you want your form builder class to be loaded after initialization (so it's reloaded on each request in development), you can pass it as a +String+ * +config.action_view.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby Logger class, which is then used to log information from Action View. Set to +nil+ to disable logging. @@ -452,9 +448,9 @@ There are a few configuration options available in Active Support: * +config.active_support.bare+ enables or disables the loading of +active_support/all+ when booting Rails. Defaults to +nil+, which means +active_support/all+ is loaded. -* +config.active_support.escape_html_entities_in_json+ enables or disables the escaping of HTML entities in JSON serialization. Defaults to +true+. +* +config.active_support.escape_html_entities_in_json+ enables or disables the escaping of HTML entities in JSON serialization. Defaults to +false+. -* +config.active_support.use_standard_json_time_format+ enables or disables serializing dates to ISO 8601 format. Defaults to +false+. +* +config.active_support.use_standard_json_time_format+ enables or disables serializing dates to ISO 8601 format. Defaults to +true+. * +ActiveSupport::BufferedLogger.silencer+ is set to +false+ to disable the ability to silence logging in a block. The default is +true+. @@ -525,6 +521,14 @@ development: password: </yaml> +Prepared Statements can be disabled thus: + +<yaml> +production: + adapter: postgresql + prepared_statements: false +</yaml> + h5. Configuring an SQLite3 Database for JRuby Platform If you choose to use SQLite3 and are using JRuby, your +config/database.yml+ will look a little different. Here's the development section: @@ -585,15 +589,15 @@ TIP: If you have any ordering dependency in your initializers, you can control t h3. Initialization events -Rails has 5 initialization events which can be hooked into (listed in the order that they are ran): +Rails has 5 initialization events which can be hooked into (listed in the order that they are run): * +before_configuration+: This is run as soon as the application constant inherits from +Rails::Application+. The +config+ calls are evaluated before this happens. * +before_initialize+: This is run directly before the initialization process of the application occurs with the +:bootstrap_hook+ initializer near the beginning of the Rails initialization process. -* +to_prepare+: Run after the initializers are ran for all Railties (including the application itself), but before eager loading and the middleware stack is built. More importantly, will run upon every request in +development+, but only once (during boot-up) in +production+ and +test+. +* +to_prepare+: Run after the initializers are run for all Railties (including the application itself), but before eager loading and the middleware stack is built. More importantly, will run upon every request in +development+, but only once (during boot-up) in +production+ and +test+. -* +before_eager_load+: This is run directly before eager loading occurs, which is the default behaviour for the _production_ environment and not for the +development+ environment. +* +before_eager_load+: This is run directly before eager loading occurs, which is the default behaviour for the +production+ environment and not for the +development+ environment. * +after_initialize+: Run directly after the initialization of the application, but before the application initializers are run. @@ -732,7 +736,7 @@ The error occurred while evaluating nil.each *+load_config_initializers+* Loads all Ruby files from +config/initializers+ in the application, railties and engines. The files in this directory can be used to hold configuration settings that should be made after all of the frameworks are loaded. -*+engines_blank_point+* Provides a point-in-initialization to hook into if you wish to do anything before engines are loaded. After this point, all railtie and engine initializers are ran. +*+engines_blank_point+* Provides a point-in-initialization to hook into if you wish to do anything before engines are loaded. After this point, all railtie and engine initializers are run. *+add_generator_templates+* Finds templates for generators at +lib/templates+ for the application, railities and engines and adds these to the +config.generators.templates+ setting, which will make the templates available for all generators to reference. diff --git a/guides/source/contributing_to_ruby_on_rails.textile b/guides/source/contributing_to_ruby_on_rails.textile index d0dbb1555a..df475a2359 100644 --- a/guides/source/contributing_to_ruby_on_rails.textile +++ b/guides/source/contributing_to_ruby_on_rails.textile @@ -42,7 +42,7 @@ h4. 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: -* "Everyday Git":http://www.kernel.org/pub/software/scm/git/docs/everyday.html will teach you just enough about git to get by. +* "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. @@ -105,6 +105,13 @@ $ cd railties $ TEST_DIR=generators bundle exec rake test </shell> +You can run any single test separately too: + +<shell> +$ cd actionpack +$ 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. @@ -201,6 +208,12 @@ $ bundle exec rake test will now run the four of them in turn. +You can also run any single test separately: + +<shell> +$ ARCONN=sqlite3 ruby -Itest test/cases/associations/has_many_associations_test.rb +</shell> + 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. Older Versions of Ruby on Rails diff --git a/guides/source/debugging_rails_applications.textile b/guides/source/debugging_rails_applications.textile index 57c7786636..45fa4ada78 100644 --- a/guides/source/debugging_rails_applications.textile +++ b/guides/source/debugging_rails_applications.textile @@ -124,7 +124,7 @@ h4. Log Levels When something is logged it's printed into the corresponding log if the log level of the message is equal or higher than the configured log level. If you want to know the current log level you can call the +Rails.logger.level+ method. -The available log levels are: +:debug+, +:info+, +:warn+, +:error+, and +:fatal+, corresponding to the log level numbers from 0 up to 4 respectively. To change the default log level, use +The available log levels are: +:debug+, +:info+, +:warn+, +:error+, +:fatal+, and +:unknown+, corresponding to the log level numbers from 0 up to 5 respectively. To change the default log level, use <ruby> config.log_level = :warn # In any environment initializer, or @@ -191,7 +191,7 @@ 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. -h3. Debugging with +ruby-debug+ +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. @@ -199,17 +199,13 @@ The debugger can also help you if you want to learn about the Rails source code h4. Setup -The debugger used by Rails, +ruby-debug+, comes as a gem. To install it, just run: +Rails uses the +debugger+ gem to set breakpoints and step through live code. To install it, just run: <shell> -$ sudo gem install ruby-debug +$ gem install debugger </shell> -TIP: If you are using Ruby 1.9, you can install a compatible version of +ruby-debug+ by running +sudo gem install ruby-debug19+ - -In case you want to download a particular version or get the source code, refer to the "project's page on rubyforge":http://rubyforge.org/projects/ruby-debug/. - -Rails has had built-in support for ruby-debug since Rails 2.0. Inside any Rails application you can invoke the debugger by calling the +debugger+ method. +Rails has had built-in support for debugging since Rails 2.0. Inside any Rails application you can invoke the debugger by calling the +debugger+ method. Here's an example: @@ -238,11 +234,11 @@ $ rails server --debugger ... </shell> -TIP: In development mode, you can dynamically +require \'ruby-debug\'+ instead of restarting the server, if it was started without +--debugger+. +TIP: In development mode, you can dynamically +require \'debugger\'+ instead of restarting the server, if it was started without +--debugger+. h4. The Shell -As soon as your application calls the +debugger+ method, the debugger will be started in a debugger shell inside the terminal window where you launched your application server, and you will be placed at ruby-debug's prompt +(rdb:n)+. The _n_ is the thread number. The prompt will also show you the next line of code that is waiting to run. +As soon as your application calls the +debugger+ method, the debugger will be started in a debugger shell inside the terminal window where you launched your application server, and you will be placed at the debugger's prompt +(rdb:n)+. The _n_ is the thread number. The prompt will also show you the next line of code that is waiting to run. If you got there by a browser request, the browser tab containing the request will be hung until the debugger has finished and the trace has finished processing the entire request. @@ -270,7 +266,7 @@ continue edit frame method putl set tmate where TIP: To view the help menu for any command use +help <command-name>+ in active debug mode. For example: _+help var+_ -The next command to learn is one of the most useful: +list+. You can also abbreviate ruby-debug commands by supplying just enough letters to distinguish them from other commands, so you can also use +l+ for the +list+ command. +The next command to learn is one of the most useful: +list+. You can abbreviate any debugging command by supplying just enough letters to distinguish them from other commands, so you can also use +l+ for the +list+ command. This command shows you where you are in the code by printing 10 lines centered around the current line; the current line in this particular case is line 6 and is marked by +=>+. @@ -347,7 +343,7 @@ h4. The Context When you start debugging your application, you will be placed in different contexts as you go through the different parts of the stack. -ruby-debug creates a context when a stopping point or an event is reached. The context has information about the suspended program which enables a debugger to inspect the frame stack, evaluate variables from the perspective of the debugged program, and contains information about the place where the debugged program is stopped. +The debugger creates a context when a stopping point or an event is reached. The context has information about the suspended program which enables a debugger to inspect the frame stack, evaluate variables from the perspective of the debugged program, and contains information about the place where the debugged program is stopped. At any time you can call the +backtrace+ command (or its alias +where+) to print the backtrace of the application. This can be very helpful to know how you got where you are. If you ever wondered about how you got somewhere in your code, then +backtrace+ will supply the answer. @@ -463,7 +459,7 @@ h4. Step by Step Now you should know where you are in the running trace and be able to print the available variables. But lets continue and move on with the application execution. -Use +step+ (abbreviated +s+) to continue running your program until the next logical stopping point and return control to ruby-debug. +Use +step+ (abbreviated +s+) to continue running your program until the next logical stopping point and return control to the debugger. TIP: You can also use <tt>step<plus> n</tt> and <tt>step- n</tt> to move forward or backward +n+ steps respectively. @@ -485,12 +481,12 @@ class Author < ActiveRecord::Base end </ruby> -TIP: You can use ruby-debug while using +rails console+. Just remember to +require "ruby-debug"+ before calling the +debugger+ method. +TIP: You can use the debugger while using +rails console+. Just remember to +require "debugger"+ before calling the +debugger+ method. <shell> $ rails console Loading development environment (Rails 3.1.0) ->> require "ruby-debug" +>> require "debugger" => [] >> author = Author.first => #<Author id: 1, first_name: "Bob", last_name: "Smith", created_at: "2008-07-31 12:46:10", updated_at: "2008-07-31 12:46:10"> @@ -603,7 +599,7 @@ A simple quit tries to terminate all threads in effect. Therefore your server wi h4. Settings -There are some settings that can be configured in ruby-debug to make it easier to debug your code. Here are a few of the available options: +The +debugger+ gem can automatically show the code you're stepping through and reload it when you change it in an editor. Here are a few of the available options: * +set reload+: Reload source code when changed. * +set autolist+: Execute +list+ command on every breakpoint. @@ -612,7 +608,7 @@ There are some settings that can be configured in ruby-debug to make it easier t You can see the full list by using +help set+. Use +help set _subcommand_+ to learn about a particular +set+ command. -TIP: You can include any number of these configuration lines inside a +.rdebugrc+ file in your HOME directory. ruby-debug will read this file every time it is loaded and configure itself accordingly. +TIP: You can save these settings in an +.rdebugrc+ file in your home directory. The debugger reads these global settings when it starts. Here's a good start for an +.rdebugrc+: @@ -637,7 +633,7 @@ If a Ruby object does not go out of scope, the Ruby Garbage Collector won't swee To install it run: <shell> -$ sudo gem install bleak_house +$ gem install bleak_house </shell> Then setup your application for profiling. Then add the following at the bottom of config/environment.rb: @@ -703,11 +699,12 @@ There are some Rails plugins to help you to find errors and debug your applicati h3. References * "ruby-debug Homepage":http://www.datanoise.com/ruby-debug +* "debugger Homepage":http://github.com/cldwalker/debugger * "Article: Debugging a Rails application with ruby-debug":http://www.sitepoint.com/article/debug-rails-app-ruby-debug/ * "ruby-debug Basics screencast":http://brian.maybeyoureinsane.net/blog/2007/05/07/ruby-debug-basics-screencast/ -* "Ryan Bate's ruby-debug screencast":http://railscasts.com/episodes/54-debugging-with-ruby-debug -* "Ryan Bate's stack trace screencast":http://railscasts.com/episodes/24-the-stack-trace -* "Ryan Bate's logger screencast":http://railscasts.com/episodes/56-the-logger +* "Ryan Bates' debugging ruby (revised) screencast":http://railscasts.com/episodes/54-debugging-ruby-revised +* "Ryan Bates' stack trace screencast":http://railscasts.com/episodes/24-the-stack-trace +* "Ryan Bates' logger screencast":http://railscasts.com/episodes/56-the-logger * "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 diff --git a/guides/source/engines.textile b/guides/source/engines.textile index 047f9afd76..880be57fb5 100644 --- a/guides/source/engines.textile +++ b/guides/source/engines.textile @@ -16,7 +16,7 @@ Engines can be considered miniature applications that provide functionality to t Therefore, engines and applications can be thought of almost the same thing, just with very minor differences, as you'll see throughout this guide. Engines and applications also share a common structure. -Engines are also closely related to plugins where the two share a common +lib+ directory structure and are both generated using the +rails plugin new+ generator. The difference being that an engine is considered a "full plugin" by Rails -- as indicated by the +--full+ option that's passed to the generator command -- but this guide will refer to them simply as "engines" throughout. An engine *can* be a plugin, and a plugin *can* be an engine. +Engines are also closely related to plugins where the two share a common +lib+ directory structure and are both generated using the +rails plugin new+ generator. The difference being that an engine is considered a "full plugin" by Rails as indicated by the +--full+ option that's passed to the generator command, but this guide will refer to them simply as "engines" throughout. An engine *can* be a plugin, and a plugin *can* be an engine. The engine that will be created in this guide will be called "blorgh". The engine will provide blogging functionality to its host applications, allowing for new posts and comments to be created. At the beginning of this guide, you will be working solely within the engine itself, but in later sections you'll see how to hook it into an application. @@ -51,7 +51,7 @@ 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+: <ruby> - gem 'blorgh', :path => "vendor/engines/blorgh" +gem 'blorgh', :path => "vendor/engines/blorgh" </ruby> By specifying it as a gem within the +Gemfile+, Bundler will load it as such, parsing this +blorgh.gemspec+ file and requiring a file within the +lib+ directory called +lib/blorgh.rb+. This file requires the +blorgh/engine.rb+ file (located at +lib/blorgh/engine.rb+) and defines a base module called +Blorgh+. @@ -77,7 +77,7 @@ 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 hte 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 overriden 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 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. @@ -115,7 +115,6 @@ The +test+ directory is where tests for the engine will go. To test the engine, <ruby> Rails.application.routes.draw do - mount Blorgh::Engine => "/blorgh" end </ruby> @@ -126,7 +125,7 @@ Also in the test directory is the +test/integration+ directory, where integratio 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 will provide 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 @@ -179,7 +178,6 @@ After that, a line for the resource is inserted into the +config/routes.rb+ file <ruby> Blorgh::Engine.routes.draw do resources :posts - end </ruby> @@ -219,17 +217,13 @@ By default, the scaffold styling is not applied to the engine as the engine's la <%= stylesheet_link_tag "scaffold" %> </erb> -You can see what the engine has so far by running +rake db:migrate+ at the root of our engine to run the migration generated by the scaffold generator, and then running +rails server+ in +test/dummy+. When you open +http://localhost:3000/blorgh/posts+ you will see the default scaffold that has been generated. - -!images/engines_scaffold.png(Blank engine scaffold)! - -Click around! You've just generated your first engine's first functions. +You can see what the engine has so far by running +rake db:migrate+ at the root of our engine to run the migration generated by the scaffold generator, and then running +rails server+ in +test/dummy+. When you open +http://localhost:3000/blorgh/posts+ you will see the default scaffold that has been generated. Click around! You've just generated your first engine's first functions. If you'd rather play around in the console, +rails console+ will also work just like a Rails application. Remember: the +Post+ model is namespaced, so to reference it you must call it as +Blorgh::Post+. <ruby> - >> Blorgh::Post.find(1) - => #<Blorgh::Post id: 1 ...> +>> Blorgh::Post.find(1) +=> #<Blorgh::Post id: 1 ...> </ruby> One final thing is that the +posts+ resource for this engine should be the root of the engine. Whenever someone goes to the root path where the engine is mounted, they should be shown a list of posts. This can be made to happen if this line is inserted into the +config/routes.rb+ file inside the engine: @@ -355,11 +349,11 @@ end This is the final part required to get the new comment form working. Displaying the comments however, is not quite right yet. If you were to create a comment right now you would see this error: -<text> - Missing partial blorgh/comments/comment with {:handlers=>[:erb, :builder], :formats=>[:html], :locale=>[:en, :en]}. Searched in: - * "/Users/ryan/Sites/side_projects/blorgh/test/dummy/app/views" - * "/Users/ryan/Sites/side_projects/blorgh/app/views" -</text> +<plain> +Missing partial blorgh/comments/comment with {:handlers=>[:erb, :builder], :formats=>[:html], :locale=>[:en, :en]}. Searched in: + * "/Users/ryan/Sites/side_projects/blorgh/test/dummy/app/views" + * "/Users/ryan/Sites/side_projects/blorgh/app/views" +</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. @@ -454,6 +448,8 @@ rake db:migrate SCOPE=blorgh VERSION=0 h4. Using a class provided by the application +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. @@ -511,11 +507,11 @@ $ rake blorgh:install:migrations Notice here that only _one_ migration was copied over here. This is because the first two migrations were copied over the first time this command was run. -<shell> - NOTE: Migration [timestamp]_create_blorgh_posts.rb from blorgh has been skipped. Migration with the same name already exists. - NOTE: Migration [timestamp]_create_blorgh_comments.rb from blorgh has been skipped. Migration with the same name already exists. - Copied migration [timestamp]_add_author_id_to_blorgh_posts.rb from blorgh -</shell> +<plain> +NOTE Migration [timestamp]_create_blorgh_posts.rb from blorgh has been skipped. Migration with the same name already exists. +NOTE Migration [timestamp]_create_blorgh_comments.rb from blorgh has been skipped. Migration with the same name already exists. +Copied migration [timestamp]_add_author_id_to_blorgh_posts.rb from blorgh +</plain> Run this migration using this command: @@ -536,9 +532,9 @@ Finally, the author's name should be displayed on the post's page. Add this code By outputting +@post.author+ using the +<%=+ tag the +to_s+ method will be called on the object. By default, this will look quite ugly: -<text> +<plain> #<User:0x00000100ccb3b0> -</text> +</plain> This is undesirable and it would be much better to have the user's name there. To do this, add a +to_s+ method to the +User+ class within the application: @@ -550,6 +546,19 @@ end Now instead of the ugly Ruby object output the author's name will be displayed. +h5. Using a controller provided by the application + +Because Rails controllers generally share code for things like authentication and accessing session variables, by default they inherit from <tt>ApplicationController</tt>. Rails engines, however are scoped to run independently from the main application, so each engine gets a scoped +ApplicationController+. This namespace prevents code collisions, but often engine controllers should access methods in the main application's +ApplicationController+. An easy way to provide this access is to change the engine's scoped +ApplicationController+ to inherit from the main application's +ApplicationController+. For our Blorgh engine this would be done by changing +app/controllers/blorgh/application_controller.rb+ to look like: + +<ruby> +class Blorgh::ApplicationController < ApplicationController +end +</ruby> + +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+. + 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. @@ -581,9 +590,9 @@ self.author = Blorgh.user_class.constantize.find_or_create_by_name(author_name) To save having to call +constantize+ on the +user_class+ result all the time, you could instead just override the +user_class+ getter method inside the +Blorgh+ module in the +lib/blorgh.rb+ file to always call +constantize+ on the saved value before returning the result: <ruby> - def self.user_class - @@user_class.constantize - end +def self.user_class + @@user_class.constantize +end </ruby> This would then turn the above code for +self.author=+ into this: @@ -663,10 +672,6 @@ Try this now by creating a new file at +app/views/blorgh/posts/index.html.erb+ a <% end %> </erb> -Rather than looking like the default scaffold, the page will now look like this: - -!images/engines_post_override.png(Engine scaffold overriden)! - h4. Routes Routes inside an engine are, by default, isolated from the application. This is done by the +isolate_namespace+ call inside the +Engine+ class. This essentially means that the application and its engines can have identically named routes, and that they will not clash. @@ -674,9 +679,9 @@ Routes inside an engine are, by default, isolated from the application. This is Routes inside an engine are drawn on the +Engine+ class within +config/routes.rb+, like this: <ruby> - Blorgh::Engine.routes.draw do - resources :posts - end +Blorgh::Engine.routes.draw do + resources :posts +end </ruby> By having isolated routes such as this, if you wish to link to an area of an engine from within an application, you will need to use the engine's routing proxy method. Calls to normal routing methods such as +posts_path+ may end up going to undesired locations if both the application and the engine both have such a helper defined. @@ -705,7 +710,7 @@ If a template is rendered from within an engine and it's attempting to use one o h4. Assets -Assets within an engine work in an identical way to a full application. Because the engine class inherits from +Rails::Engine+, the application will know to look up in the engine's +app/assets+ directory for potential assets. +Assets within an engine work in an identical way to a full application. Because the engine class inherits from +Rails::Engine+, the application will know to look up in the engine's +app/assets+ and +lib/assets+ directories for potential assets. Much like all the other components of an engine, the assets should also be namespaced. This means if you have an asset called +style.css+, it should be placed at +app/assets/stylesheets/[engine name]/style.css+, rather than +app/assets/stylesheets/style.css+. If this asset wasn't namespaced, then there is a possibility that the host application could have an asset named identically, in which case the application's asset would take precedence and the engine's one would be all but ignored. @@ -717,11 +722,11 @@ Imagine that you did have an asset located at +app/assets/stylesheets/blorgh/sty You can also specify these assets as dependencies of other assets using the Asset Pipeline require statements in processed files: -<css> +<plain> /* *= require blorgh/style */ -</css> +</plain> h4. Separate Assets & Precompiling @@ -736,7 +741,7 @@ You can define assets for precompilation in +engine.rb+ initializer do |app| app.config.assets.precompile += %w(admin.css admin.js) end -</ruby +</ruby> For more information, read the "Asset Pipeline guide":http://guides.rubyonrails.org/asset_pipeline.html diff --git a/guides/source/form_helpers.textile b/guides/source/form_helpers.textile index 8934667c5e..033b33ec3b 100644 --- a/guides/source/form_helpers.textile +++ b/guides/source/form_helpers.textile @@ -89,7 +89,7 @@ form_tag(:controller => "people", :action => "search", :method => "get", :class # => '<form accept-charset="UTF-8" action="/people/search?method=get&class=nifty_form" method="post">' </ruby> -Here, +method+ and +class+ are appended to the query string of the generated URL because you even though you mean to write two hashes, you really only specified one. So you need to tell Ruby which is which by delimiting the first hash (or both) with curly brackets. This will generate the HTML you expect: +Here, +method+ and +class+ are appended to the query string of the generated URL because even though you mean to write two hashes, you really only specified one. So you need to tell Ruby which is which by delimiting the first hash (or both) with curly brackets. This will generate the HTML you expect: <ruby> form_tag({:controller => "people", :action => "search"}, :method => "get", :class => "nifty_form") @@ -150,7 +150,7 @@ NOTE: Always use labels for checkbox and radio buttons. They associate text with h4. Other Helpers of Interest -Other form controls worth mentioning are textareas, password fields, hidden fields, search fields, telephone fields, date fields, URL fields and email fields: +Other form controls worth mentioning are textareas, password fields, hidden fields, search fields, telephone fields, date fields, time fields, URL fields and email fields: <erb> <%= text_area_tag(:message, "Hi, nice site", :size => "24x6") %> @@ -161,6 +161,7 @@ Other form controls worth mentioning are textareas, password fields, hidden fiel <%= date_field(:user, :born_on) %> <%= url_field(:user, :homepage) %> <%= email_field(:user, :address) %> +<%= time_field(:task, :started_at) %> </erb> Output: @@ -174,11 +175,12 @@ Output: <input id="user_born_on" name="user[born_on]" type="date" /> <input id="user_homepage" name="user[homepage]" type="url" /> <input id="user_address" name="user[address]" type="email" /> +<input id="task_started_at" name="task[started_at]" type="time" /> </html> Hidden inputs are not shown to the user but instead hold data like any textual input. Values inside them can be changed with JavaScript. -IMPORTANT: The search, telephone, date, URL, and email inputs are HTML5 controls. If you require your app to have a consistent experience in older browsers, you will need an HTML5 polyfill (provided by CSS and/or JavaScript). There is definitely "no shortage of solutions for this":https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills, although a couple of popular tools at the moment are "Modernizr":http://www.modernizr.com/ and "yepnope":http://yepnopejs.com/, which provide a simple way to add functionality based on the presence of detected HTML5 features. +IMPORTANT: The search, telephone, date, time, URL, and email inputs are HTML5 controls. If you require your app to have a consistent experience in older browsers, you will need an HTML5 polyfill (provided by CSS and/or JavaScript). There is definitely "no shortage of solutions for this":https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills, although a couple of popular tools at the moment are "Modernizr":http://www.modernizr.com/ and "yepnope":http://yepnopejs.com/, which provide a simple way to add functionality based on the presence of detected HTML5 features. TIP: If you're using password input fields (for any purpose), you might want to configure your application to prevent those parameters from being logged. You can learn about this in the "Security Guide":security.html#logging. @@ -405,6 +407,8 @@ Whenever Rails sees that the internal value of an option being generated matches TIP: The second argument to +options_for_select+ must be exactly equal to the desired internal value. In particular if the value is the integer 2 you cannot pass "2" to +options_for_select+ -- you must pass 2. Be aware of values extracted from the +params+ hash as they are all strings. +WARNING: when +:inlude_blank+ or +:prompt:+ are not present, +:include_blank+ is forced true if the select attribute +required+ is true, display +size+ is one and +multiple+ is not true. + h4. Select Boxes for Dealing with Models In most cases form controls will be tied to a specific database model and as you might expect Rails provides helpers tailored for that purpose. Consistent with other form helpers, when dealing with models you drop the +_tag+ suffix from +select_tag+: @@ -469,7 +473,7 @@ Rails _used_ to have a +country_select+ helper for choosing countries, but this h3. Using Date and Time Form Helpers -You can choose not to use the form helpers generating HTML5 date input fields and use the alternative date and time helpers. These date and time helpers differ from all the other form helpers in two important respects: +You can choose not to use the form helpers generating HTML5 date and time input fields and use the alternative date and time helpers. These date and time helpers differ from all the other form helpers in two important respects: # Dates and times are not representable by a single input element. Instead you have several, one for each component (year, month, day etc.) and so there is no single value in your +params+ hash with your date or time. # Other helpers use the +_tag+ suffix to indicate whether a helper is a barebones helper or one that operates on model objects. With dates and times, +select_date+, +select_time+ and +select_datetime+ are the barebones helpers, +date_select+, +time_select+ and +datetime_select+ are the equivalent model object helpers. diff --git a/guides/source/generators.textile b/guides/source/generators.textile index 920ff997ae..2e9ab0526d 100644 --- a/guides/source/generators.textile +++ b/guides/source/generators.textile @@ -451,6 +451,27 @@ Adds a specified source to +Gemfile+: add_source "http://gems.github.com" </ruby> +h4. +inject_into_file+ + +Injects a block of code into a defined position in your file. + +<ruby> +inject_into_file 'name_of_file.rb', :after => "#The code goes below this line. Don't forget the Line break at the end\n" do <<-'RUBY' + puts "Hello World" +RUBY +end +</ruby> + +h4. +gsub_file+ + +Replaces text inside a file. + +<ruby> +gsub_file 'name_of_file.rb', 'method.to_be_replaced', 'method.the_replacing_code' +</ruby> + +Regular Expressions can be used to make this method more precise. You can also use append_file and prepend_file in the same way to place code at the beginning and end of a file respectively. + h4. +application+ Adds a line to +config/application.rb+ directly after the application class definition. diff --git a/guides/source/getting_started.textile b/guides/source/getting_started.textile index 0a85c84155..19bd106ff0 100644 --- a/guides/source/getting_started.textile +++ b/guides/source/getting_started.textile @@ -10,7 +10,7 @@ you should be familiar with: endprologue. -WARNING. This Guide is based on Rails 3.1. Some of the code shown here will not +WARNING. This Guide is based on Rails 3.2. Some of the code shown here will not work in earlier versions of Rails. WARNING: The Edge version of this guide is currently being re-worked. Please excuse us while we re-arrange the place. @@ -27,8 +27,8 @@ prerequisites installed: TIP: Note that Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails 3.0. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02 though. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults -on Rails 3.0, so if you want to use Rails 3 with 1.9.x jump on 1.9.2 for smooth -sailing. +on Rails 3.0, so if you want to use Rails 3 with 1.9.x jump on 1.9.2 or +1.9.3 for smooth sailing. * The "RubyGems":http://rubyforge.org/frs/?group_id=126 packaging system ** If you want to learn more about RubyGems, please read the "RubyGems User Guide":http://docs.rubygems.org/read/book/1 @@ -45,10 +45,6 @@ internet for learning Ruby, including: h3. What is Rails? -TIP: This section goes into the background and philosophy of the Rails framework -in detail. You can safely skip this section and come back to it at a later time. -Section 3 starts you on the path to creating your first Rails application. - Rails is a web application development framework written in the Ruby language. It is designed to make programming web applications easier by making assumptions about what every developer needs to get started. It allows you to write less @@ -73,9 +69,11 @@ h3. Creating a New Rails Project The best way to use this guide is to follow each step as it happens, no code or step needed to make this example application has been left out, so you can -literally follow along step by step. You can get the complete code "here":https://github.com/lifo/docrails/tree/master/guides/code/getting_started. +literally follow along step by step. You can get the complete code +"here":https://github.com/lifo/docrails/tree/master/guides/code/getting_started. -By following along with this guide, you'll create a Rails project called <tt>blog</tt>, a +By following along with this guide, you'll create a Rails project called ++blog+, a (very) simple weblog. Before you can start building the application, you need to make sure that you have Rails itself installed. @@ -89,7 +87,10 @@ To install Rails, use the +gem install+ command provided by RubyGems: # gem install rails </shell> -TIP. If you're working on Windows, you can quickly install Ruby and Rails with "Rails Installer":http://railsinstaller.org. +TIP. A number of tools exist to help you quickly install Ruby and Ruby +on Rails on your system. Windows users can use "Rails +Installer":http://railsinstaller.org, while Mac OS X users can use +"Rails One Click":http://railsoneclick.com. To verify that you have everything installed correctly, you should be able to run the following: @@ -97,7 +98,7 @@ To verify that you have everything installed correctly, you should be able to ru $ rails --version </shell> -If it says something like "Rails 3.2.2" you are ready to continue. +If it says something like "Rails 3.2.3" you are ready to continue. h4. Creating the Blog Application @@ -111,7 +112,8 @@ $ rails new blog This will create a Rails application called Blog in a directory called blog. -TIP: You can see all of the switches that the Rails application builder accepts by running <tt>rails new -h</tt>. +TIP: You can see all of the command line options that the Rails +application builder accepts by running +rails new -h+. After you create the blog application, switch to its folder to continue work directly in that application: @@ -119,10 +121,13 @@ After you create the blog application, switch to its folder to continue work dir $ cd blog </shell> -The +rails new blog+ command we ran above created a folder in your working directory called <tt>blog</tt>. The <tt>blog</tt> directory has a number of auto-generated folders that make up the structure of a Rails application. Most of the work in this tutorial will happen in the <tt>app/</tt> folder, but here's a basic rundown on the function of each of the files and folders that Rails created by default: +The +rails new blog+ command we ran above created a folder in your +working directory called +blog+. The +blog+ directory has a number of +auto-generated files and folders that make up the structure of a Rails +application. Most of the work in this tutorial will happen in the +app/+ folder, but here's a basic rundown on the function of each of the files and folders that Rails created by default: |_.File/Folder|_.Purpose| -|app/|Contains the controllers, models, views and assets for your application. You'll focus on this folder for the remainder of this guide.| +|app/|Contains the controllers, models, views, helpers, mailers and assets for your application. You'll focus on this folder for the remainder of this guide.| |config/|Configure your application's runtime rules, routes, database, and more. This is covered in more detail in "Configuring Rails Applications":configuring.html| |config.ru|Rack configuration for Rack based servers used to start the application.| |db/|Contains your current database schema, as well as the database migrations.| @@ -140,7 +145,7 @@ The +rails new blog+ command we ran above created a folder in your working direc h3. Hello, Rails! -One of the traditional places to start with a new language is by getting some text up on screen quickly. To do this, you need to get your Rails application server running. +To begin with, let's get some text up on screen quickly. To do this, you need to get your Rails application server running. h4. Starting up the Web Server @@ -152,11 +157,11 @@ $ rails server TIP: Compiling CoffeeScript to JavaScript requires a JavaScript runtime and the absence of a runtime will give you an +execjs+ error. Usually Mac OS X and Windows come with a JavaScript runtime installed. Rails adds the +therubyracer+ gem to Gemfile in a commented line for new apps and you can uncomment if you need it. +therubyrhino+ is the recommended runtime for JRuby users and is added by default to Gemfile in apps generated under JRuby. You can investigate about all the supported runtimes at "ExecJS":https://github.com/sstephenson/execjs#readme. -This will fire up an instance of a webserver built into Ruby called WEBrick by default. To see your application in action, open a browser window and navigate to "http://localhost:3000":http://localhost:3000. You should see Rails' default information page: +This will fire up WEBrick, a webserver built into Ruby by default. To see your application in action, open a browser window and navigate to "http://localhost:3000":http://localhost:3000. You should see the Rails default information page: !images/rails_welcome.png(Welcome Aboard screenshot)! -TIP: To stop the web server, hit Ctrl+C in the terminal window where it's running. In development mode, Rails does not generally require you to stop the server; changes you make in files will be automatically picked up by the server. +TIP: To stop the web server, hit Ctrl+C in the terminal window where it's running. In development mode, Rails does not generally require you to restart the server; changes you make in files will be automatically picked up by the server. The "Welcome Aboard" page is the _smoke test_ for a new Rails application: it makes sure that you have your software configured correctly enough to serve a page. You can also click on the _About your application’s environment_ link to see a summary of your application's environment. @@ -164,7 +169,7 @@ h4. Say "Hello", Rails To get Rails saying "Hello", you need to create at minimum a _controller_ and a _view_. -A controller's purpose is to receive specific requests for the application. What controller receives what request is determined by the _routing_. There is very often more than one route to each controller, and different routes can be served by different _actions_. Each action's purpose is to collect information to provide it to a view. +A controller's purpose is to receive specific requests for the application. _Routing_ decides which controller receives which requests. Often, there is more than one route to each controller, and different routes can be served by different _actions_. Each action's purpose is to collect information to provide it to a view. A view's purpose is to display this information in a human readable format. An important distinction to make is that it is the _controller_, not the view, where information is collected. The view should just display that information. By default, view templates are written in a language called ERB (Embedded Ruby) which is converted by the request cycle in Rails before being sent to the user. @@ -178,9 +183,9 @@ Rails will create several files for you. Most important of these are of course t Open the +app/views/welcome/index.html.erb+ file in your text editor and edit it to contain a single line of code: -<code class="html"> +<html> <h1>Hello, Rails!</h1> -</code> +</html> h4. Setting the Application Home Page @@ -188,7 +193,7 @@ Now that we have made the controller and view, we need to tell Rails when we wan To fix this, delete the +index.html+ file located inside the +public+ directory of the application. -You need to do this because Rails will serve any static file in the +public+ directory that matches a route in preference to any dynamic content you generate from the controllers. +You need to do this because Rails will serve any static file in the +public+ directory that matches a route in preference to any dynamic content you generate from the controllers. The +index.html+ file is special: it will be served if a request comes in at the root route, e.g. http://localhost:3000. If another request such as http://localhost:3000/welcome happened, a static file at <tt>public/welcome.html</tt> would be served first, but only if it existed. Next, you have to tell Rails where your actual home page is located. @@ -205,7 +210,7 @@ Blog::Application.routes.draw do The +root :to => "welcome#index"+ tells Rails to map requests to the root of the application to the welcome controller's index action. This was created earlier when you ran the controller generator (+rails generate controller welcome index+). -If you navigate to "http://localhost:3000":http://localhost:3000 in your browser, you'll see +Hello, Rails!+. +If you navigate to "http://localhost:3000":http://localhost:3000 in your browser, you'll see the +Hello, Rails!+ message you put into +app/views/welcome/index.html.erb+, indicating that this new route is indeed going to +WelcomeController+'s +index+ action and is rendering the view correctly. NOTE. For more information about routing, refer to "Rails Routing from the Outside In":routing.html. @@ -215,7 +220,7 @@ Now that you've seen how to create a controller, an action and a view, let's cre In the Blog application, you will now create a new _resource_. A resource is the term used for a collection of similar objects, such as posts, people or animals. You can create, read, update and destroy items for a resource and these operations are referred to as _CRUD_ operations. -In the next section, you will add the ability to create new posts in your application and be able to view them. This is the "CR" from CRUD. The form for doing this will look like this: +In the next section, you will add the ability to create new posts in your application and be able to view them. This is the "C" and the "R" from CRUD: creation and reading. The form for doing this will look like this: !images/getting_started/new_post.png(The new post form)! @@ -225,10 +230,9 @@ h4. Laying down the ground work The first thing that you are going to need to create a new post within the application is a place to do that. A great place for that would be at +/posts/new+. If you attempt to navigate to that now -- by visiting "http://localhost:3000/posts/new":http://localhost:3000/posts/new -- Rails will give you a routing error: - !images/getting_started/routing_error_no_route_matches.png(A routing error, no route matches /posts/new)! -This is because there is nowhere inside the routes for the application -- defined inside +config/routes.rb+ -- that defines this route. By default, Rails has no routes configured at all, and so you must define your routes as you need them. +This is because there is nowhere inside the routes for the application -- defined inside +config/routes.rb+ -- that defines this route. By default, Rails has no routes configured at all, besides the root route you defined earlier, and so you must define your routes as you need them. To do this, you're going to need to create a route inside +config/routes.rb+ file, on a new line between the +do+ and the +end+ for the +draw+ method: @@ -240,7 +244,7 @@ This route is a super-simple route: it defines a new route that only responds to With the route defined, requests can now be made to +/posts/new+ in the application. Navigate to "http://localhost:3000/posts/new":http://localhost:3000/posts/new and you'll see another routing error: -!images/getting_started/routing_error_no_controller.png(Another routing error, uninitialized constant PostsController) +!images/getting_started/routing_error_no_controller.png(Another routing error, uninitialized constant PostsController)! This error is happening because this route need a controller to be defined. The route is attempting to find that controller so it can serve the request, but with the controller undefined, it just can't do that. The solution to this particular problem is simple: you need to create a controller called +PostsController+. You can do this by running this command: @@ -259,7 +263,7 @@ A controller is simply a class that is defined to inherit from +ApplicationContr If you refresh "http://localhost:3000/posts/new":http://localhost:3000/posts/new now, you'll get a new error: -!images/getting_started/unknown_action_new_for_posts.png(Unknown action new for PostsController!) +!images/getting_started/unknown_action_new_for_posts.png(Unknown action new for PostsController!)! This error indicates that Rails cannot find the +new+ action inside the +PostsController+ that you just generated. This is because when controllers are generated in Rails they are empty by default, unless you tell it you wanted actions during the generation process. @@ -272,25 +276,25 @@ end With the +new+ method defined in +PostsController+, if you refresh "http://localhost:3000/posts/new":http://localhost:3000/posts/new you'll see another error: -!images/getting_started/template_is_missing_posts_new.png(Template is missing for posts/new) +!images/getting_started/template_is_missing_posts_new.png(Template is missing for posts/new)! You're getting this error now because Rails expects plain actions like this one to have views associated with them to display their information. With no view available, Rails errors out. In the above image, the bottom line has been truncated. Let's see what the full thing looks like: -<text> +<blockquote> Missing template posts/new, application/new with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder, :coffee]}. Searched in: * "/path/to/blog/app/views" -</text> +</blockquote> That's quite a lot of text! Let's quickly go through and understand what each part of it does. -The first part identifies what template is missing. In this case, it's the +posts/new+ template. Rails will first look for this template. If it can't find it, then it will attempt to load a template called +application/new+. It looks for one here because the +PostsController+ inherits from +ApplicationController+. +The first part identifies what template is missing. In this case, it's the +posts/new+ template. Rails will first look for this template. If not found, then it will attempt to load a template called +application/new+. It looks for one here because the +PostsController+ inherits from +ApplicationController+. -The next part of the message contains a hash. The +:locale+ key in this hash simply indicates what spoken language template should be retrieved. By default, this is the English -- or "en" -- template. The next key, +:formats+ shows what formats of template Rails is after. The default is +:html+, and so Rails is looking for an HTML template. The final key, +:handlers+, is telling us what _template handlers_ could be used to render our template. +:erb+ is most commonly used for HTML templates, +:builder+ is used for XML templates, and +:coffee+ uses CoffeeScript to build JavaScript templates. +The next part of the message contains a hash. The +:locale+ key in this hash simply indicates what spoken language template should be retrieved. By default, this is the English -- or "en" -- template. The next key, +:formats+ specifies the format of template to be served in response . The default format is +:html+, and so Rails is looking for an HTML template. The final key, +:handlers+, is telling us what _template handlers_ could be used to render our template. +:erb+ is most commonly used for HTML templates, +:builder+ is used for XML templates, and +:coffee+ uses CoffeeScript to build JavaScript templates. The final part of this message tells us where Rails has looked for the templates. Templates within a basic Rails application like this are kept in a single location, but in more complex applications it could be many different paths. -The simplest template that would work in this case would be one located at +app/views/posts/new.html.erb+. The extension of this file name is key: the first extension is the _format_ of the template, and the second extension is the _handler_ that will be used. Rails is attempting to find a template called +posts/new+ within +app/views+ for the application. The format for this template can only be +html+ and the handler must be one of +erb+, +builder+ or +coffee+. Because you want to create a new HTML form, you will be using the +ERB+ language. Therefore the file should be called +posts/new.html.erb+ and be located inside the +app/views+ directory of the application. +The simplest template that would work in this case would be one located at +app/views/posts/new.html.erb+. The extension of this file name is key: the first extension is the _format_ of the template, and the second extension is the _handler_ that will be used. Rails is attempting to find a template called +posts/new+ within +app/views+ for the application. The format for this template can only be +html+ and the handler must be one of +erb+, +builder+ or +coffee+. Because you want to create a new HTML form, you will be using the +ERB+ language. Therefore the file should be called +posts/new.html.erb+ and needs to be located inside the +app/views+ directory of the application. Go ahead now and create a new file at +app/views/posts/new.html.erb+ and write this content in it: @@ -302,7 +306,9 @@ When you refresh "http://localhost:3000/posts/new":http://localhost:3000/posts/n h4. The first form -To create a form within this template, you will use a _form builder_. The primary form builder for Rails is provided by a helper method called +form_for+. To use this method, write this code into +app/views/posts/new.html.erb+: +To create a form within this template, you will use a <em>form +builder</em>. The primary form builder for Rails is provided by a helper +method called +form_for+. To use this method, add this code into +app/views/posts/new.html.erb+: <erb> <%= form_for :post do |f| %> @@ -324,11 +330,17 @@ To create a form within this template, you will use a _form builder_. The primar If you refresh the page now, you'll see the exact same form as in the example. Building forms in Rails is really just that easy! -When you call +form_for+, you pass it an identifying object for this form. In this case, it's the symbol +:post+. This tells the +form_for+ helper what this form is for. Inside the block for this method, the FormBuilder object -- represented by +f+ -- is used to build two labels and two text fields, one each for the title and text of a post. Finally, a call to +submit+ on the +f+ object will create a submit button for the form. +When you call +form_for+, you pass it an identifying object for this +form. In this case, it's the symbol +:post+. This tells the +form_for+ +helper what this form is for. Inside the block for this method, the ++FormBuilder+ object -- represented by +f+ -- is used to build two labels and two text fields, one each for the title and text of a post. Finally, a call to +submit+ on the +f+ object will create a submit button for the form. There's one problem with this form though. If you inspect the HTML that is generated, by viewing the source of the page, you will see that the +action+ attribute for the form is pointing at +/posts/new+. This is a problem because this route goes to the very page that you're on right at the moment, and that route should only be used to display the form for a new post. -So the form needs to use a different URL in order to go somewhere else. This can be done quite simply with the +:url+ option of +form_for+. Typically in Rails, the action that is used for new form submissions like this is called "create", and so the form should be pointed to this action. +The form needs to use a different URL in order to go somewhere else. +This can be done quite simply with the +:url+ option of +form_for+. +Typically in Rails, the action that is used for new form submissions +like this is called "create", and so the form should be pointed to that action. Edit the +form_for+ line inside +app/views/posts/new.html.erb+ to look like this: @@ -344,11 +356,11 @@ post "posts/create" 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. -With the form and the route for it defined now, you will be able to fill in the form and then click the submit button to begin the process of creating a new post, so go ahead and do that. When you submit the form, you should see a familiar error: +With the form and its associated route defined, you will be able to fill in the form and then click the submit button to begin the process of creating a new post, so go ahead and do that. When you submit the form, you should see a familiar error: -!images/getting_started/unknown_action_create_for_posts(Unknown action create for PostsController)! +!images/getting_started/unknown_action_create_for_posts.png(Unknown action create for PostsController)! -You will now need to create the +create+ action within the +PostsController+ for this to work. +You now need to create the +create+ action within the +PostsController+ for this to work. h4. Creating posts @@ -375,7 +387,7 @@ def create end </ruby> -The +render+ method here is taking a very simple hash with the key of +text+ and the value of +params[:post].inspect+. The +params+ method here is the object which represents the parameters (or fields) coming in from the form. The +params+ method returns a +HashWithIndifferentAccess+ object, which allows you to access the keys of the hash using either strings or symbols. In this situation, the only parameters that matter are the ones from the form. +The +render+ method here is taking a very simple hash with a key of +text+ and value of +params[:post].inspect+. The +params+ method is the object which represents the parameters (or fields) coming in from the form. The +params+ method returns a +HashWithIndifferentAccess+ object, which allows you to access the keys of the hash using either strings or symbols. In this situation, the only parameters that matter are the ones from the form. If you re-submit the form one more time you'll now no longer get the missing template error. Instead, you'll see something that looks like the following: @@ -385,25 +397,52 @@ If you re-submit the form one more time you'll now no longer get the missing tem This action is now displaying the parameters for the post that are coming in from the form. However, this isn't really all that helpful. Yes, you can see the parameters but nothing in particular is being done with them. +h4. Creating the Post model + +Models in Rails use a singular name, and their corresponding database tables use +a plural name. Rails provides a generator for creating models, which +most Rails developers tend to use when creating new models. +To create the new model, run this command in your terminal: + +<shell> +$ rails generate model Post title:string text:text +</shell> + +With that command we told Rails that we want a +Post+ model, together +with a _title_ attribute of type string, and a _text_ attribute +of type text. Those attributes are automatically added to the +posts+ +table in the database and mapped to the +Post+ model. + +Rails responded by creating a bunch of files. For +now, we're only interested in +app/models/post.rb+ and ++db/migrate/20120419084633_create_posts.rb+ (your name could be a bit +different). The latter is responsible +for creating the database structure, which is what we'll look at next. + +TIP: Active Record is smart enough to automatically map column names to +model attributes, which means you don't have to declare attributes +inside Rails models, as that will be done automatically by Active +Record. + h4. Running a Migration -One of the products of the +rails generate scaffold+ command is a _database -migration_. Migrations are Ruby classes that are designed to make it simple to +As we've just seen, +rails generate model+ created a _database +migration_ file inside the +db/migrate+ directory. +Migrations are Ruby classes that are designed to make it simple to create and modify database tables. Rails uses rake commands to run migrations, and it's possible to undo a migration after it's been applied to your database. Migration filenames include a timestamp to ensure that they're processed in the order that they were created. -If you look in the +db/migrate/20100207214725_create_posts.rb+ file (remember, +If you look in the +db/migrate/20120419084633_create_posts.rb+ file (remember, yours will have a slightly different name), here's what you'll find: <ruby> class CreatePosts < ActiveRecord::Migration def change create_table :posts do |t| - t.string :name t.string :title - t.text :content + t.text :text t.timestamps end @@ -415,7 +454,7 @@ The above migration creates a method named +change+ which will be called when yo run this migration. The action defined in this method is also reversible, which means Rails knows how to reverse the change made by this migration, in case you want to reverse it later. When you run this migration it will create a -+posts+ table with two string columns and a text column. It also creates two ++posts+ table with one string column and a text column. It also creates two timestamp fields to allow Rails to track post creation and update times. More information about Rails migrations can be found in the "Rails Database Migrations":migrations.html guide. @@ -440,43 +479,180 @@ NOTE. Because you're working in the development environment by default, this command will apply to the database defined in the +development+ section of your +config/database.yml+ file. If you would like to execute migrations in another environment, for instance in production, you must explicitly pass it when -invoking the command: <tt>rake db:migrate RAILS_ENV=production</tt>. +invoking the command: +rake db:migrate RAILS_ENV=production+. + +h4. Saving data in the controller + +Back in +posts_controller+, we need to change the +create+ action +to use the new +Post+ model to save the data in the database. Open that file +and change the +create+ action to look like this: + +<ruby> +def create + @post = Post.new(params[:post]) + + @post.save + redirect_to :action => :show, :id => @post.id +end +</ruby> + +Here's what's going on: every Rails model can be initialized with its +respective attributes, which are automatically mapped to the respective +database columns. In the first line we do just that (remember that ++params[:post]+ contains the attributes we're interested in). Then, ++@post.save+ is responsible for saving the model in the database. +Finally, we redirect the user to the +show+ action, +wich we'll define later. + +TIP: As we'll see later, +@post.save+ returns a boolean indicating +wherever the model was saved or not. + +h4. Showing Posts + +If you submit the form again now, Rails will complain about not finding +the +show+ action. That's not very useful though, so let's add the ++show+ action before proceeding. Open +config/routes.rb+ and add the following route: + +<ruby> +get "posts/:id" => "posts#show" +</ruby> + +The special syntax +:id+ tells rails that this route expects an +:id+ +parameter, which in our case will be the id of the post. Note that this +time we had to specify the actual mapping, +posts#show+ because +otherwise Rails would not know which action to render. + +As we did before, we need to add the +show+ action in the ++posts_controller+ and its respective view. + +<ruby> +def show + @post = Post.find(params[:id]) +end +</ruby> + +A couple of things to note. We use +Post.find+ to find the post we're +interested in. We also use an instance variable (prefixed by +@+) to +hold a reference to the post object. We do this because Rails will pass all instance +variables to the view. + +Now, create a new file +app/view/posts/show.html.erb+ with the following +content: + +<erb> +<p> + <strong>Title:</strong> + <%= @post.title %> +</p> + +<p> + <strong>Text:</strong> + <%= @post.text %> +</p> +</erb> -h4. Adding a Link +Finally, if you now go to +"http://localhost:3000/posts/new":http://localhost:3000/posts/new you'll +be able to create a post. Try it! -To hook the posts up to the home page you've already created, you can add a link -to the home page. Open +app/views/welcome/index.html.erb+ and modify it as follows: +!images/getting_started/show_action_for_posts.png(Show action for posts)! + +h4. Listing all posts + +We still need a way to list all our posts, so let's do that. As usual, +we'll need a route placed into +config/routes.rb+: + +<ruby> +get "posts" => "posts#index" +</ruby> + +And an action for that route inside the +PostsController+ in the +app/controllers/posts_controller.rb+ file: + +<ruby> +def index + @posts = Post.all +end +</ruby> + +And then finally a view for this action, located at +app/views/posts/index.html.erb+: + +<erb> +<h1>Listing posts</h1> + +<table> + <tr> + <th>Title</th> + <th>Text</th> + </tr> + + <% @posts.each do |post| %> + <tr> + <td><%= post.title %></td> + <td><%= post.text %></td> + </tr> + <% end %> +</table> +</erb> + +Now if you go to +http://localhost:3000/posts+ you will see a list of all the posts that you have created. + +h4. Adding links + +You can now create, show, and list posts. Now let's add some links to +navigate through pages. + +Open +app/views/welcome/index.html.erb+ and modify it as follows: <ruby> <h1>Hello, Rails!</h1> -<%= link_to "My Blog", posts_path %> +<%= link_to "My Blog", :controller => "posts" %> </ruby> The +link_to+ method is one of Rails' built-in view helpers. It creates a hyperlink based on text to display and where to go - in this case, to the path for posts. -h4. Working with Posts in the Browser +Let's add links to the other views as well, starting with adding this "New Post" link to +app/views/posts/index.html.erb+, placing it above the +<table>+ tag: + +<erb> +<%= link_to 'New post', :action => :new %> +</erb> + +This link will allow you to bring up the form that lets you create a new post. You should also add a link to this template -- +app/views/posts/new.html.erb+ -- to go back to the +index+ action. Do this by adding this underneath the form in this template: + +<erb> +<%= form_for :post do |f| %> + ... +<% end %> + +<%= link_to 'Back', :action => :index %> +</erb> + +Finally, add another link to the +app/views/posts/show.html.erb+ template to go back to the +index+ action as well, so that people who are viewing a single post can go back and view the whole list again: -Now you're ready to start working with posts. To do that, navigate to -"http://localhost:3000":http://localhost:3000/ and then click the "My Blog" -link: +<erb> +<p> + <strong>Title:</strong> + <%= @post.title %> +</p> -!images/posts_index.png(Posts Index screenshot)! +<p> + <strong>Text:</strong> + <%= @post.text %> +</p> + +<%= link_to 'Back', :action => :index %> +</erb> -This is the result of Rails rendering the +index+ view of your posts. There -aren't currently any posts in the database, but if you click the +New Post+ link -you can create one. After that, you'll find that you can edit posts, look at -their details, or destroy them. All of the logic and HTML to handle this was -built by the single +rails generate scaffold+ command. +TIP: If you want to link to an action in the same controller, you don't +need to specify the +:controller+ option, as Rails will use the current +controller by default. TIP: In development mode (which is what you're working in by default), Rails reloads your application with every browser request, so there's no need to stop -and restart the web server. +and restart the web server when a change is made. -Congratulations, you're riding the rails! Now it's time to see how it all works. - -h4. The Model +h4. Allowing the update of fields The model file, +app/models/post.rb+ is about as simple as it can get: @@ -491,6 +667,19 @@ your Rails models for free, including basic database CRUD (Create, Read, Update, Destroy) operations, data validation, as well as sophisticated search support and the ability to relate multiple models to one another. +Rails includes methods to help you secure some of your model fields. +Open the +app/models/post.rb+ file and edit it: + +<ruby> +class Post < ActiveRecord::Base + attr_accessible :text, :title +end +</ruby> + +This change will ensure that all changes made through HTML forms can edit the content of the text and title fields. +It will not be possible to define any other field value through forms. You can still define them by calling the `field=` method of course. +Accessible attributes and the mass assignment probem is covered in details in the "Security guide":security.html#mass-assignment + h4. Adding Some Validation Rails includes methods to help you validate the data that you send to models. @@ -498,205 +687,288 @@ Open the +app/models/post.rb+ file and edit it: <ruby> class Post < ActiveRecord::Base - validates :name, :presence => true + attr_accessible :text, :title + validates :title, :presence => true, :length => { :minimum => 5 } end </ruby> -These changes will ensure that all posts have a name and a title, and that the -title is at least five characters long. Rails can validate a variety of -conditions in a model, including the presence or uniqueness of columns, their +These changes will ensure that all posts have a title that is at least five characters long. +Rails can validate a variety of conditions in a model, including the presence or uniqueness of columns, their format, and the existence of associated objects. Validations are covered in detail -in "Active Record Validations and Callbacks":active_record_validations_callbacks.html#validations-overview +in "Active Record Validations and +Callbacks":active_record_validations_callbacks.html#validations-overview -h4. Using the Console +With the validation now in place, when you call +@post.save+ on an invalid +post, it will return +false+. If you open +app/controllers/posts_controller.rb+ +again, you'll notice that we don't check the result of calling +@post.save+ +inside the +create+ action. If +@post.save+ fails in this situation, we need to +show the form back to the user. To do this, change the +new+ and +create+ +actions inside +app/controllers/posts_controller.rb+ to these: -To see your validations in action, you can use the console. The console is a -command-line tool that lets you execute Ruby code in the context of your -application: +<ruby> +def new + @post = Post.new +end -<shell> -$ rails console -</shell> +def create + @post = Post.new(params[:post]) + + if @post.save + redirect_to :action => :show, :id => @post.id + else + render 'new' + end +end +</ruby> -TIP: The default console will make changes to your database. You can instead -open a console that will roll back any changes you make by using <tt>rails console ---sandbox</tt>. +The +new+ action is now creating a new instance variable called +@post+, and +you'll see why that is in just a few moments. -After the console loads, you can use it to work with your application's models: +Notice that inside the +create+ action we use +render+ instead of +redirect_to+ when +save+ +returns +false+. The +render+ method is used so that the +@post+ object is passed back to the +new+ template when it is rendered. This rendering is done within the same request as the form submission, whereas the +redirect_to+ will tell the browser to issue another request. -<shell> ->> p = Post.new(:content => "A new post") -=> #<Post id: nil, name: nil, title: nil, - content: "A new post", created_at: nil, - updated_at: nil> ->> p.save -=> false ->> p.errors.full_messages -=> ["Name can't be blank", "Title can't be blank", "Title is too short (minimum is 5 characters)"] -</shell> +If you reload +"http://localhost:3000/posts/new":http://localhost:3000/posts/new and +try to save a post without a title, Rails will send you back to the +form, but that's not very useful. You need to tell the user that +something went wrong. To do that, you'll modify ++app/views/posts/new.html.erb+ to check for error messages: + +<erb> +<%= form_for :post, :url => { :action => :create } do |f| %> + <% if @post.errors.any? %> + <div id="errorExplanation"> + <h2><%= pluralize(@post.errors.count, "error") %> prohibited + this post from being saved:</h2> + <ul> + <% @post.errors.full_messages.each do |msg| %> + <li><%= msg %></li> + <% end %> + </ul> + </div> + <% end %> + <p> + <%= f.label :title %><br> + <%= f.text_field :title %> + </p> + + <p> + <%= f.label :text %><br> + <%= f.text_area :text %> + </p> -This code shows creating a new +Post+ instance, attempting to save it and -getting +false+ for a return value (indicating that the save failed), and -inspecting the +errors+ of the post. + <p> + <%= f.submit %> + </p> +<% end %> + +<%= link_to 'Back', :action => :index %> +</erb> + +A few things are going on. We check if there are any errors with ++@post.errors.any?+, and in that case we show a list of all +errors with +@post.errors.full_messages+. + ++pluralize+ is a rails helper that takes a number and a string as its +arguments. If the number is greater than one, the string will be automatically pluralized. -When you're finished, type +exit+ and hit +return+ to exit the console. +The reason why we added +@post = Post.new+ in +posts_controller+ is that +otherwise +@post+ would be +nil+ in our view, and calling ++@post.errors.any?+ would throw an error. -TIP: Unlike the development web server, the console does not automatically load -your code afresh for each line. If you make changes to your models (in your editor) -while the console is open, type +reload!+ at the console prompt to load them. +TIP: Rails automatically wraps fields that contain an error with a div +with class +field_with_errors+. You can define a css rule to make them +standout. -h4. Listing All Posts +Now you'll get a nice error message when saving a post without title when you +attempt to do just that on the "new post form(http://localhost:3000/posts/new)":http://localhost:3000/posts/new. -Let's dive into the Rails code a little deeper to see how the application is -showing us the list of Posts. Open the file -+app/controllers/posts_controller.rb+ and look at the -+index+ action: +!images/getting_started/form_with_errors.png(Form With Errors)! + +h4. Updating Posts + +We've covered the "CR" part of CRUD. Now let's focus on the "U" part, +updating posts. + +The first step we'll take is adding a +edit+ action to ++posts_controller+. + +Start by adding a route to +config/routes.rb+: <ruby> -def index - @posts = Post.all +get "posts/:id/edit" => "posts#edit" +</ruby> + +And then add the controller action: - respond_to do |format| - format.html # index.html.erb - format.json { render :json => @posts } +<ruby> +def edit + @post = Post.find(params[:id]) +end +</ruby> + +The view will contain a form similar to the one we used when creating +new posts. Create a file called +app/views/posts/edit.html.erb+ and make +it look as follows: + +<erb> +<h1>Editing post</h1> + +<%= form_for :post, :url => { :action => :update, :id => @post.id }, +:method => :put do |f| %> + <% if @post.errors.any? %> + <div id="errorExplanation"> + <h2><%= pluralize(@post.errors.count, "error") %> prohibited + this post from being saved:</h2> + <ul> + <% @post.errors.full_messages.each do |msg| %> + <li><%= msg %></li> + <% end %> + </ul> + </div> + <% end %> + <p> + <%= f.label :title %><br> + <%= f.text_field :title %> + </p> + + <p> + <%= f.label :text %><br> + <%= f.text_area :text %> + </p> + + <p> + <%= f.submit %> + </p> +<% end %> + +<%= link_to 'Back', :action => :index %> +</erb> + +This time we point the form to the +update+ action, which is not defined yet +but will be very soon. + +The +:method => :put+ option tells Rails that we want this form to be +submitted via the +PUT+, HTTP method which is the HTTP method you're expected to use to +*update* resources according to the REST protocol. + +TIP: By default forms built with the +form_for_ helper are sent via +POST+. + +Next, we need to add the +update+ action. The file ++config/routes.rb+ will need just one more line: + +<ruby> +put "posts/:id" => "posts#update" +</ruby> + +And then create the +update+ action in +app/controllers/posts_controller.rb+: + +<ruby> +def update + @post = Post.find(params[:id]) + + if @post.update_attributes(params[:post]) + redirect_to :action => :show, :id => @post.id + else + render 'edit' end end </ruby> -+Post.all+ returns all of the posts currently in the database as an array -of +Post+ records that we store in an instance variable called +@posts+. +The new method, +update_attributes+, is used when you want to update a record +that already exists, and it accepts an hash containing the attributes +that you want to update. As before, if there was an error updating the +post we want to show the form back to the user. -TIP: For more information on finding records with Active Record, see "Active -Record Query Interface":active_record_querying.html. +TIP: you don't need to pass all attributes to +update_attributes+. For +example, if you'd call +@post.update_attributes(:title => 'A new title')+ +Rails would only update the +title+ attribute, leaving all other +attributes untouched. -The +respond_to+ block handles both HTML and JSON calls to this action. If you -browse to "http://localhost:3000/posts.json":http://localhost:3000/posts.json, -you'll see a JSON containing all of the posts. The HTML format looks for a view -in +app/views/posts/+ with a name that corresponds to the action name. Rails -makes all of the instance variables from the action available to the view. -Here's +app/views/posts/index.html.erb+: +Finally, we want to show a link to the +edit+ action in the list of all the +posts, so let's add that now to +app/views/posts/index.html.erb+ to make it +appear next to the "Show" link: <erb> -<h1>Listing posts</h1> <table> <tr> - <th>Name</th> <th>Title</th> - <th>Content</th> - <th></th> + <th>Text</th> <th></th> <th></th> </tr> <% @posts.each do |post| %> <tr> - <td><%= post.name %></td> <td><%= post.title %></td> - <td><%= post.content %></td> - <td><%= link_to 'Show', post %></td> - <td><%= link_to 'Edit', edit_post_path(post) %></td> - <td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', - :method => :delete %></td> + <td><%= post.text %></td> + <td><%= link_to 'Show', :action => :show, :id => post.id %></td> + <td><%= link_to 'Edit', :action => :edit, :id => post.id %></td> </tr> <% end %> </table> - -<br /> - -<%= link_to 'New post', new_post_path %> </erb> -This view iterates over the contents of the +@posts+ array to display content -and links. A few things to note in the view: - -* +link_to+ builds a hyperlink to a particular destination -* +edit_post_path+ and +new_post_path+ are helpers that Rails provides as part of RESTful routing. You'll see a variety of these helpers for the different actions that the controller includes. - -NOTE. In previous versions of Rails, you had to use +<%=h post.name %>+ so -that any HTML would be escaped before being inserted into the page. In Rails -3 and above, this is now the default. To get unescaped HTML, you now use <tt><%= raw post.name %></tt>. - -TIP: For more details on the rendering process, see "Layouts and Rendering in -Rails":layouts_and_rendering.html. - -h4. Customizing the Layout - -The view is only part of the story of how HTML is displayed in your web browser. -Rails also has the concept of +layouts+, which are containers for views. When -Rails renders a view to the browser, it does so by putting the view's HTML into -a layout's HTML. In previous versions of Rails, the +rails generate scaffold+ -command would automatically create a controller specific layout, like -+app/views/layouts/posts.html.erb+, for the posts controller. However this has -been changed in Rails 3. An application specific +layout+ is used for all the -controllers and can be found in +app/views/layouts/application.html.erb+. Open -this layout in your editor and modify the +body+ tag to include the style directive -below: +And we'll also add one to the +app/views/posts/show.html.erb+ template as well, +so that there's also an "Edit" link on a post's page. Add this at the bottom of +the template: <erb> -<!DOCTYPE html> -<html> -<head> - <title>Blog</title> - <%= stylesheet_link_tag "application" %> - <%= javascript_include_tag "application" %> - <%= csrf_meta_tags %> -</head> -<body style="background-color: #EEEEEE;"> +... -<%= yield %> -</body> -</html> +<%= link_to 'Back', :action => :index %> +| <%= link_to 'Edit', :action => :edit, :id => @post.id %> </erb> -Now when you refresh the +/posts+ page, you'll see a gray background to the -page. This same gray background will be used throughout all the views. +And here's how our app looks so far: -h4. Creating New Posts +!images/getting_started/index_action_with_edit_link.png(Index action +with edit link)! -Creating a new post involves two actions. The first is the +new+ action, which -instantiates an empty +Post+ object: +h4. Using partials to clean up duplication in views -<ruby> -def new - @post = Post.new ++partials+ are what Rails uses to remove duplication in views. Here's a +simple example: - respond_to do |format| - format.html # new.html.erb - format.json { render :json => @post } - end -end -</ruby> +<erb> +# app/views/user/show.html.erb -The +new.html.erb+ view displays this empty Post to the user: +<h1><%= @user.name %></h1> -<erb> -<h1>New post</h1> +<%= render 'user_details' %> -<%= render 'form' %> +# app/views/user/_user_details.html.erb + +<%= @user.location %> -<%= link_to 'Back', posts_path %> +<%= @user.about_me %> </erb> -The +<%= render 'form' %>+ line is our first introduction to _partials_ in -Rails. A partial is a snippet of HTML and Ruby code that can be reused in -multiple locations. In this case, the form used to make a new post is basically -identical to the form used to edit a post, both having text fields for the name and -title, a text area for the content, and a button to create the new post or to update -the existing one. +The +users/show+ template will automatically include the content of the ++users/_user_details+ template. Note that partials are prefixed by an underscore, +as to not be confused with regular views. However, you don't include the +underscore when including them with the +helper+ method. -If you take a look at +views/posts/_form.html.erb+ file, you will see the -following: +TIP: You can read more about partials in the "Layouts and Rendering in +Rails":layouts_and_rendering.html guide. + +Our +edit+ action looks very similar to the +new+ action, in fact they +both share the same code for displaying the form. Lets clean them up by +using a partial. + +Create a new file +app/views/posts/_form.html.erb+ with the following +content: <erb> -<%= form_for(@post) do |f| %> +<%= form_for @post do |f| %> <% if @post.errors.any? %> <div id="errorExplanation"> <h2><%= pluralize(@post.errors.count, "error") %> prohibited - this post from being saved:</h2> + this post from being saved:</h2> <ul> <% @post.errors.full_messages.each do |msg| %> <li><%= msg %></li> @@ -704,230 +976,238 @@ following: </ul> </div> <% end %> - - <div class="field"> - <%= f.label :name %><br /> - <%= f.text_field :name %> - </div> - <div class="field"> - <%= f.label :title %><br /> + <p> + <%= f.label :title %><br> <%= f.text_field :title %> - </div> - <div class="field"> - <%= f.label :content %><br /> - <%= f.text_area :content %> - </div> - <div class="actions"> + </p> + + <p> + <%= f.label :text %><br> + <%= f.text_area :text %> + </p> + + <p> <%= f.submit %> - </div> + </p> <% end %> </erb> -This partial receives all the instance variables defined in the calling view -file. In this case, the controller assigned the new +Post+ object to +@post+, -which will thus be available in both the view and the partial as +@post+. +Everything except for the +form_for+ declaration remained the same. +How +form_for+ can figure out the right +action+ and +method+ attributes +when building the form will be explained in just a moment. For now, let's update the ++app/views/posts/new.html.erb+ view to use this new partial, rewriting it +completely: -For more information on partials, refer to the "Layouts and Rendering in -Rails":layouts_and_rendering.html#using-partials guide. +<erb> +<h1>New post</h1> -The +form_for+ block is used to create an HTML form. Within this block, you have -access to methods to build various controls on the form. For example, -+f.text_field :name+ tells Rails to create a text input on the form and to hook -it up to the +name+ attribute of the instance being displayed. You can only use -these methods with attributes of the model that the form is based on (in this -case +name+, +title+, and +content+). Rails uses +form_for+ in preference to -having you write raw HTML because the code is more succinct, and because it -explicitly ties the form to a particular model instance. +<%= render 'form' %> -The +form_for+ block is also smart enough to work out if you are doing a _New -Post_ or an _Edit Post_ action, and will set the form +action+ tags and submit -button names appropriately in the HTML output. +<%= link_to 'Back', :action => :index %> +</erb> -TIP: If you need to create an HTML form that displays arbitrary fields, not tied -to a model, you should use the +form_tag+ method, which provides shortcuts for -building forms that are not necessarily tied to a model instance. +Then do the same for the +app/views/posts/edit.html.erb+ view: -When the user clicks the +Create Post+ button on this form, the browser will -send information back to the +create+ action of the controller (Rails knows to -call the +create+ action because the form is sent with an HTTP POST request; -that's one of the conventions that were mentioned earlier): +<erb> +<h1>Edit post</h1> -<ruby> -def create - @post = Post.new(params[:post]) +<%= render 'form' %> - respond_to do |format| - if @post.save - format.html { redirect_to(@post, - :notice => 'Post was successfully created.') } - format.json { render :json => @post, - :status => :created, :location => @post } - else - format.html { render :action => "new" } - format.json { render :json => @post.errors, - :status => :unprocessable_entity } - end - end -end -</ruby> +<%= link_to 'Back', :action => :index %> +</erb> -The +create+ action instantiates a new Post object from the data supplied by the -user on the form, which Rails makes available in the +params+ hash. After -successfully saving the new post, +create+ returns the appropriate format that -the user has requested (HTML in our case). It then redirects the user to the -resulting post +show+ action and sets a notice to the user that the Post was -successfully created. - -If the post was not successfully saved, due to a validation error, then the -controller returns the user back to the +new+ action with any error messages so -that the user has the chance to fix the error and try again. - -The "Post was successfully created." message is stored in the Rails -+flash+ hash (usually just called _the flash_), so that messages can be carried -over to another action, providing the user with useful information on the status -of their request. In the case of +create+, the user never actually sees any page -rendered during the post creation process, because it immediately redirects to -the new +Post+ as soon as Rails saves the record. The Flash carries over a message to -the next action, so that when the user is redirected back to the +show+ action, -they are presented with a message saying "Post was successfully created." - -h4. Showing an Individual Post - -When you click the +show+ link for a post on the index page, it will bring you -to a URL like +http://localhost:3000/posts/1+. Rails interprets this as a call -to the +show+ action for the resource, and passes in +1+ as the +:id+ parameter. -Here's the +show+ action: +Point your browser to "http://localhost:3000/posts/new":http://localhost:3000/posts/new and +try creating a new post. Everything still works. Now try editing the +post and you'll receive the following error: + +!images/getting_started/undefined_method_post_path.png(Undefined method +post_path)! + +To understand this error, you need to understand how +form_for+ works. +When you pass an object to +form_for+ and you don't specify a +:url+ +option, Rails will try to guess the +action+ and +method+ options by +checking if the passed object is a new record or not. Rails follows the +REST convention, so to create a new +Post+ object it will look for a +route named +posts_path+, and to update a +Post+ object it will look for +a route named +post_path+ and pass the current object. Similarly, rails +knows that it should create new objects via POST and update them via +PUT. + +If you run +rake routes+ from the console you'll see that we already +have a +posts_path+ route, which was created automatically by Rails when we +defined the route for the index action. +However, we don't have a +post_path+ yet, which is the reason why we +received an error before. -<ruby> -def show - @post = Post.find(params[:id]) +<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 +</shell> - respond_to do |format| - format.html # show.html.erb - format.json { render :json => @post } - end -end -</ruby> +To fix this, open +config/routes.rb+ and modify the +get "posts/:id"+ +line like this: -The +show+ action uses +Post.find+ to search for a single record in the database -by its id value. After finding the record, Rails displays it by using -+app/views/posts/show.html.erb+: +<ruby> +get "posts/:id" => "posts#show", :as => :post +</ruby> -<erb> -<p id="notice"><%= notice %></p> +The +:as+ option tells the +get+ method that we want to make routing helpers +called +post_url+ and +post_path+ available to our application. These are +precisely the methods that the +form_for+ needs when editing a post, and so now +you'll be able to update posts again. -<p> - <b>Name:</b> - <%= @post.name %> -</p> +NOTE: The +:as+ option is available on the +post+, +put+, +delete+ and +match+ +routing methods also. -<p> - <b>Title:</b> - <%= @post.title %> -</p> +h4. Deleting Posts -<p> - <b>Content:</b> - <%= @post.content %> -</p> +We're now ready to cover the "D" part of CRUD, deleting posts from the +database. Following the REST convention, we're going to add a route for +deleting posts to +config/routes.rb+: +<ruby> +delete "posts/:id" => "posts#destroy" +</ruby> -<%= link_to 'Edit', edit_post_path(@post) %> | -<%= link_to 'Back', posts_path %> -</erb> +The +delete+ routing method should be used for routes that destroy +resources. If this was left as a typical +get+ route, it could be possible for +people to craft malicious URLs like this: -h4. Editing Posts +<html> +<a href='http://yoursite.com/posts/1/destroy'>look at this cat!</a> +</html> -Like creating a new post, editing a post is a two-part process. The first step -is a request to +edit_post_path(@post)+ with a particular post. This calls the -+edit+ action in the controller: +We use the +delete+ method for destroying resources, and this route is mapped to +the +destroy+ action inside +app/controllers/posts_controller.rb+, which doesn't exist yet, but is +provided below: <ruby> -def edit +def destroy @post = Post.find(params[:id]) + @post.destroy + + redirect_to :action => :index end </ruby> -After finding the requested post, Rails uses the +edit.html.erb+ view to display -it: +You can call +destroy+ on Active Record objects when you want to delete +them from the database. Note that we don't need to add a view for this +action since we're redirecting to the +index+ action. -<erb> -<h1>Editing post</h1> +Finally, add a 'destroy' link to your +index+ action template +(+app/views/posts/index.html.erb) to wrap everything +together. -<%= render 'form' %> +<erb> +<h1>Listing Posts</h1> +<table> + <tr> + <th>Title</th> + <th>Text</th> + <th></th> + <th></th> + <th></th> + </tr> -<%= link_to 'Show', @post %> | -<%= link_to 'Back', posts_path %> +<% @posts.each do |post| %> + <tr> + <td><%= post.title %></td> + <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> + </tr> +<% end %> +</table> </erb> -Again, as with the +new+ action, the +edit+ action is using the +form+ partial. -This time, however, the form will do a PUT action to the +PostsController+ and the -submit button will display "Update Post". +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+ +options are used as html5 attributes so that when the click is linked, +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. -Submitting the form created by this view will invoke the +update+ action within -the controller: +!images/getting_started/confirm_dialog.png(Confirm Dialog)! -<ruby> -def update - @post = Post.find(params[:id]) +Congratulations, you can now create, show, list, update and destroy +posts. In the next section will see how Rails can aid us when creating +REST applications, and how we can refactor our Blog app to take +advantage of it. - respond_to do |format| - if @post.update_attributes(params[:post]) - format.html { redirect_to(@post, - :notice => 'Post was successfully updated.') } - format.json { head :no_content } - else - format.html { render :action => "edit" } - format.json { render :json => @post.errors, - :status => :unprocessable_entity } - end - end -end -</ruby> +h4. Going Deeper into REST -In the +update+ action, Rails first uses the +:id+ parameter passed back from -the edit view to locate the database record that's being edited. The -+update_attributes+ call then takes the +post+ parameter (a hash) from the request -and applies it to this record. If all goes well, the user is redirected to the -post's +show+ action. If there are any problems, it redirects back to the +edit+ action to -correct them. +We've now covered all the CRUD actions of a REST app. We did so by +declaring separate routes with the appropriate verbs into ++config/routes.rb+. Here's how that file looks so far: -h4. Destroying a Post +<ruby> +get "posts" => "posts#index" +get "posts/new" +post "posts/create" +get "posts/:id" => "posts#show", :as => :post +get "posts/:id/edit" => "posts#edit" +put "posts/:id" => "posts#update" +delete "posts/:id" => "posts#destroy" +</ruby> -Finally, clicking one of the +destroy+ links sends the associated id to the -+destroy+ action: +That's a lot to type for covering a single *resource*. Fortunately, +Rails provides a +resources+ method which can be used to declare a +standard REST resource. Here's how +config/routes/rb+ looks after the +cleanup: <ruby> -def destroy - @post = Post.find(params[:id]) - @post.destroy +Blog::Application.routes.draw do - respond_to do |format| - format.html { redirect_to posts_url } - format.json { head :no_content } - end + resources :posts + + root :to => "welcome#index" end </ruby> -The +destroy+ method of an Active Record model instance removes the -corresponding record from the database. After that's done, there isn't any -record to display, so Rails redirects the user's browser to the index action of -the controller. +If you run +rake routes+, you'll see that all the routes that we +declared before are still available: + +<shell> +# rake routes + posts GET /posts(.:format) posts#index + POST /posts(.:format) posts#create + new_post GET /posts/new(.:format) posts#new +edit_post GET /posts/:id/edit(.:format) posts#edit + post GET /posts/:id(.:format) posts#show + PUT /posts/:id(.:format) posts#update + DELETE /posts/:id(.:format) posts#destroy + root / welcome#index +</shell> + +Also, if you go through the motions of creating, updating and deleting +posts the app still works as before. + +TIP: In general, Rails encourages the use of resources objects in place +of declaring routes manually. It was only done in this guide as a learning +exercise. For more information about routing, see +"Rails Routing from the Outside In":routing.html. h3. Adding a Second Model -Now that you've seen what a model built with scaffolding looks like, it's time to -add a second model to the application. The second model will handle comments on -blog posts. +It's time to add a second model to the application. The second model will handle comments on +posts. h4. Generating a Model -Models in Rails use a singular name, and their corresponding database tables use -a plural name. For the model to hold comments, the convention is to use the name -+Comment+. Even if you don't want to use the entire apparatus set up by -scaffolding, most Rails developers still use generators to make things like -models and controllers. To create the new model, run this command in your -terminal: +We're going to see the same generator that we used before when creating +the +Post+ model. This time we'll create a +Comment+ model to hold +reference of post comments. Run this command in your terminal: <shell> $ rails generate model Comment commenter:string body:text post:references @@ -1015,7 +1295,6 @@ You'll need to edit the +post.rb+ file to add the other side of the association: <ruby> class Post < ActiveRecord::Base - validates :name, :presence => true validates :title, :presence => true, :length => { :minimum => 5 } @@ -1034,9 +1313,7 @@ h4. Adding a Route for Comments As with the +welcome+ controller, we will need to add a route so that Rails knows where we would like to navigate to see +comments+. Open up the -+config/routes.rb+ file again. Near the top, you will see the entry for +posts+ -that was added automatically by the scaffold generator: <tt>resources -:posts</tt>. Edit it as follows: ++config/routes.rb+ file again, and edit it as follows: <ruby> resources :posts do @@ -1054,7 +1331,7 @@ In":routing.html guide. h4. Generating a Controller With the model in hand, you can turn your attention to creating a matching -controller. Again, there's a generator for this: +controller. Again, we'll use the same generator we used before: <shell> $ rails generate controller Comments @@ -1081,44 +1358,40 @@ So first, we'll wire up the Post show template (+/app/views/posts/show.html.erb+) to let us make a new comment: <erb> -<p id="notice"><%= notice %></p> - <p> - <b>Name:</b> - <%= @post.name %> -</p> - -<p> - <b>Title:</b> + <strong>Title:</strong> <%= @post.title %> </p> <p> - <b>Content:</b> - <%= @post.content %> + <strong>Text:</strong> + <%= @post.text %> </p> <h2>Add a comment:</h2> <%= form_for([@post, @post.comments.build]) do |f| %> - <div class="field"> + <p> <%= f.label :commenter %><br /> <%= f.text_field :commenter %> - </div> - <div class="field"> + </p> + <p> <%= f.label :body %><br /> <%= f.text_area :body %> - </div> - <div class="actions"> + </p> + <p> <%= f.submit %> - </div> + </p> <% end %> <%= link_to 'Edit Post', edit_post_path(@post) %> | -<%= link_to 'Back to Posts', posts_path %> | +<%= link_to 'Back to Posts', posts_path %> </erb> This adds a form on the +Post+ show page that creates a new comment by -calling the +CommentsController+ +create+ action. Let's wire that up: +calling the +CommentsController+ +create+ action. The +form_for+ call here uses +an array, which will build a nested route, such as +/posts/1/comments+. + +Let's wire up the +create+: <ruby> class CommentsController < ApplicationController @@ -1147,60 +1420,53 @@ template. This is where we want the comment to show, so let's add that to the +app/views/posts/show.html.erb+. <erb> -<p id="notice"><%= notice %></p> - <p> - <b>Name:</b> - <%= @post.name %> -</p> - -<p> - <b>Title:</b> + <strong>Title:</strong> <%= @post.title %> </p> <p> - <b>Content:</b> - <%= @post.content %> + <strong>Text:</strong> + <%= @post.text %> </p> <h2>Comments</h2> <% @post.comments.each do |comment| %> <p> - <b>Commenter:</b> + <strong>Commenter:</strong> <%= comment.commenter %> </p> <p> - <b>Comment:</b> + <strong>Comment:</strong> <%= comment.body %> </p> <% end %> <h2>Add a comment:</h2> <%= form_for([@post, @post.comments.build]) do |f| %> - <div class="field"> + <p> <%= f.label :commenter %><br /> <%= f.text_field :commenter %> - </div> - <div class="field"> + </p> + <p> <%= f.label :body %><br /> <%= f.text_area :body %> - </div> - <div class="actions"> + </p> + <p> <%= f.submit %> - </div> + </p> <% end %> -<br /> - <%= link_to 'Edit Post', edit_post_path(@post) %> | -<%= link_to 'Back to Posts', posts_path %> | +<%= link_to 'Back to Posts', posts_path %> </erb> Now you can add posts and comments to your blog and have them show up in the right places. +!images/getting_started/post_with_comments.png(Post with Comments)! + h3. Refactoring Now that we have posts and comments working, take a look at the @@ -1209,18 +1475,18 @@ use partials to clean it up. h4. Rendering Partial Collections -First we will make a comment partial to extract showing all the comments for the +First, we will make a comment partial to extract showing all the comments for the post. Create the file +app/views/comments/_comment.html.erb+ and put the following into it: <erb> <p> - <b>Commenter:</b> + <strong>Commenter:</strong> <%= comment.commenter %> </p> <p> - <b>Comment:</b> + <strong>Comment:</strong> <%= comment.body %> </p> </erb> @@ -1229,21 +1495,14 @@ Then you can change +app/views/posts/show.html.erb+ to look like the following: <erb> -<p id="notice"><%= notice %></p> - -<p> - <b>Name:</b> - <%= @post.name %> -</p> - <p> - <b>Title:</b> + <strong>Title:</strong> <%= @post.title %> </p> <p> - <b>Content:</b> - <%= @post.content %> + <strong>Text:</strong> + <%= @post.text %> </p> <h2>Comments</h2> @@ -1251,23 +1510,21 @@ following: <h2>Add a comment:</h2> <%= form_for([@post, @post.comments.build]) do |f| %> - <div class="field"> + <p> <%= f.label :commenter %><br /> <%= f.text_field :commenter %> - </div> - <div class="field"> + </p> + <p> <%= f.label :body %><br /> <%= f.text_area :body %> - </div> - <div class="actions"> + </p> + <p> <%= f.submit %> - </div> + </p> <% end %> -<br /> - <%= link_to 'Edit Post', edit_post_path(@post) %> | -<%= link_to 'Back to Posts', posts_path %> | +<%= link_to 'Back to Posts', posts_path %> </erb> This will now render the partial in +app/views/comments/_comment.html.erb+ once @@ -1283,50 +1540,38 @@ create a file +app/views/comments/_form.html.erb+ containing: <erb> <%= form_for([@post, @post.comments.build]) do |f| %> - <div class="field"> + <p> <%= f.label :commenter %><br /> <%= f.text_field :commenter %> - </div> - <div class="field"> + </p> + <p> <%= f.label :body %><br /> <%= f.text_area :body %> - </div> - <div class="actions"> + </p> + <p> <%= f.submit %> - </div> + </p> <% end %> </erb> Then you make the +app/views/posts/show.html.erb+ look like the following: <erb> -<p id="notice"><%= notice %></p> - -<p> - <b>Name:</b> - <%= @post.name %> -</p> - <p> - <b>Title:</b> + <strong>Title:</strong> <%= @post.title %> </p> <p> - <b>Content:</b> - <%= @post.content %> + <strong>Text:</strong> + <%= @post.text %> </p> -<h2>Comments</h2> -<%= render @post.comments %> - <h2>Add a comment:</h2> <%= render "comments/form" %> -<br /> - <%= link_to 'Edit Post', edit_post_path(@post) %> | -<%= link_to 'Back to Posts', posts_path %> | +<%= link_to 'Back to Posts', posts_path %> </erb> The second render just defines the partial template we want to render, @@ -1348,12 +1593,12 @@ So first, let's add the delete link in the <erb> <p> - <b>Commenter:</b> + <strong>Commenter:</strong> <%= comment.commenter %> </p> <p> - <b>Comment:</b> + <strong>Comment:</strong> <%= comment.body %> </p> @@ -1402,7 +1647,6 @@ model, +app/models/post.rb+, as follows: <ruby> class Post < ActiveRecord::Base - validates :name, :presence => true validates :title, :presence => true, :length => { :minimum => 5 } has_many :comments, :dependent => :destroy @@ -1431,11 +1675,8 @@ class PostsController < ApplicationController http_basic_authenticate_with :name => "dhh", :password => "secret", :except => [:index, :show] - # GET /posts - # GET /posts.json def index @posts = Post.all - respond_to do |format| # snipped for brevity </ruby> @@ -1457,213 +1698,6 @@ Authentication challenge !images/challenge.png(Basic HTTP Authentication Challenge)! -h3. Building a Multi-Model Form - -Another feature of your average blog is the ability to tag posts. To implement -this feature your application needs to interact with more than one model on a -single form. Rails offers support for nested forms. - -To demonstrate this, we will add support for giving each post multiple tags, -right in the form where you create the post. First, create a new model to hold -the tags: - -<shell> -$ rails generate model Tag name:string post:references -</shell> - -Again, run the migration to create the database table: - -<shell> -$ rake db:migrate -</shell> - -Next, edit the +post.rb+ file to create the other side of the association, and -to tell Rails (via the +accepts_nested_attributes_for+ macro) that you intend to -edit tags via posts: - -<ruby> -class Post < ActiveRecord::Base - validates :name, :presence => true - validates :title, :presence => true, - :length => { :minimum => 5 } - - has_many :comments, :dependent => :destroy - has_many :tags - - accepts_nested_attributes_for :tags, :allow_destroy => :true, - :reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } } -end -</ruby> - -The +:allow_destroy+ option tells Rails to enable destroying tags through the -nested attributes (you'll handle that by displaying a "remove" checkbox on the -view that you'll build shortly). The +:reject_if+ option prevents saving new -tags that do not have any attributes filled in. - -We will modify +views/posts/_form.html.erb+ to render a partial to make a tag: - -<erb> -<% @post.tags.build %> -<%= form_for(@post) do |post_form| %> - <% if @post.errors.any? %> - <div id="errorExplanation"> - <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2> - <ul> - <% @post.errors.full_messages.each do |msg| %> - <li><%= msg %></li> - <% end %> - </ul> - </div> - <% end %> - - <div class="field"> - <%= post_form.label :name %><br /> - <%= post_form.text_field :name %> - </div> - <div class="field"> - <%= post_form.label :title %><br /> - <%= post_form.text_field :title %> - </div> - <div class="field"> - <%= post_form.label :content %><br /> - <%= post_form.text_area :content %> - </div> - <h2>Tags</h2> - <%= render :partial => 'tags/form', - :locals => {:form => post_form} %> - <div class="actions"> - <%= post_form.submit %> - </div> -<% end %> -</erb> - -Note that we have changed the +f+ in +form_for(@post) do |f|+ to +post_form+ to -make it easier to understand what is going on. - -This example shows another option of the render helper, being able to pass in -local variables, in this case, we want the local variable +form+ in the partial -to refer to the +post_form+ object. - -We also add a <tt>@post.tags.build</tt> at the top of this form. This is to make -sure there is a new tag ready to have its name filled in by the user. If you do -not build the new tag, then the form will not appear as there is no new Tag -object ready to create. - -Now create the folder <tt>app/views/tags</tt> and make a file in there called -<tt>_form.html.erb</tt> which contains the form for the tag: - -<erb> -<%= form.fields_for :tags do |tag_form| %> - <div class="field"> - <%= tag_form.label :name, 'Tag:' %> - <%= tag_form.text_field :name %> - </div> - <% unless tag_form.object.nil? || tag_form.object.new_record? %> - <div class="field"> - <%= tag_form.label :_destroy, 'Remove:' %> - <%= tag_form.check_box :_destroy %> - </div> - <% end %> -<% end %> -</erb> - -Finally, we will edit the <tt>app/views/posts/show.html.erb</tt> template to -show our tags. - -<erb> -<p id="notice"><%= notice %></p> - -<p> - <b>Name:</b> - <%= @post.name %> -</p> - -<p> - <b>Title:</b> - <%= @post.title %> -</p> - -<p> - <b>Content:</b> - <%= @post.content %> -</p> - -<p> - <b>Tags:</b> - <%= @post.tags.map { |t| t.name }.join(", ") %> -</p> - -<h2>Comments</h2> -<%= render @post.comments %> - -<h2>Add a comment:</h2> -<%= render "comments/form" %> - - -<%= link_to 'Edit Post', edit_post_path(@post) %> | -<%= link_to 'Back to Posts', posts_path %> | -</erb> - -With these changes in place, you'll find that you can edit a post and its tags -directly on the same view. - -However, that method call <tt>@post.tags.map { |t| t.name }.join(", ")</tt> is -awkward, we could handle this by making a helper method. - -h3. View Helpers - -View Helpers live in <tt>app/helpers</tt> and provide small snippets of reusable -code for views. In our case, we want a method that strings a bunch of objects -together using their name attribute and joining them with a comma. As this is -for the Post show template, we put it in the PostsHelper. - -Open up <tt>app/helpers/posts_helper.rb</tt> and add the following: - -<erb> -module PostsHelper - def join_tags(post) - post.tags.map { |t| t.name }.join(", ") - end -end -</erb> - -Now you can edit the view in <tt>app/views/posts/show.html.erb</tt> to look like -this: - -<erb> -<p id="notice"><%= notice %></p> - -<p> - <b>Name:</b> - <%= @post.name %> -</p> - -<p> - <b>Title:</b> - <%= @post.title %> -</p> - -<p> - <b>Content:</b> - <%= @post.content %> -</p> - -<p> - <b>Tags:</b> - <%= join_tags(@post) %> -</p> - -<h2>Comments</h2> -<%= render @post.comments %> - -<h2>Add a comment:</h2> -<%= render "comments/form" %> - - -<%= link_to 'Edit Post', edit_post_path(@post) %> | -<%= link_to 'Back to Posts', posts_path %> | -</erb> - h3. What's Next? Now that you've seen your first Rails application, you should feel free to diff --git a/guides/source/i18n.textile b/guides/source/i18n.textile index 320f1e9d20..ee7176a6c8 100644 --- a/guides/source/i18n.textile +++ b/guides/source/i18n.textile @@ -127,7 +127,7 @@ If you want to translate your Rails application to a *single language other than However, you would probably like to *provide support for more locales* in your application. In such case, you need to set and pass the locale between requests. -WARNING: You may be tempted to store the chosen locale in a _session_ or a <em>cookie</em>. *Do not do so*. The locale should be transparent and a part of the URL. This way you don't break people's basic assumptions about the web itself: if you send a URL of some page to a friend, she should see the same page, same content. A fancy word for this would be that you're being "<em>RESTful</em>":http://en.wikipedia.org/wiki/Representational_State_Transfer. Read more about the RESTful approach in "Stefan Tilkov's articles":http://www.infoq.com/articles/rest-introduction. There may be some exceptions to this rule, which are discussed below. +WARNING: You may be tempted to store the chosen locale in a _session_ or a <em>cookie</em>, however *do not do this*. The locale should be transparent and a part of the URL. This way you won't break people's basic assumptions about the web itself: if you send a URL to a friend, they should see the same page and content as you. A fancy word for this would be that you're being "<em>RESTful</em>":http://en.wikipedia.org/wiki/Representational_State_Transfer. Read more about the RESTful approach in "Stefan Tilkov's articles":http://www.infoq.com/articles/rest-introduction. Sometimes there are exceptions to this rule and those are discussed below. The _setting part_ is easy. You can set the locale in a +before_filter+ in the +ApplicationController+ like this: @@ -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 Netherlands 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 "+path_prefix+":http://api.rubyonrails.org/classes/ActionController/Resources.html#M000354 option in this way: <ruby> # config/routes.rb @@ -229,7 +229,7 @@ scope "/:locale" do end </ruby> -Now, when you call the +books_path+ method you should get +"/en/books"+ (for the default locale). An URL like +http://localhost:3001/nl/books+ should load the Netherlands locale, then, and following calls to +books_path+ should return +"/nl/books"+ (because the locale changed). +Now, when you call the +books_path+ method you should get +"/en/books"+ (for the default locale). An URL like +http://localhost:3001/nl/books+ should load the Dutch locale, then, and following calls to +books_path+ should return +"/nl/books"+ (because the locale changed). If you don't want to force the use of a locale in your routes you can use an optional path scope (denoted by the parentheses) like so: @@ -866,19 +866,35 @@ The I18n API will catch all of these exceptions when they are thrown in the back The reason for this is that during development you'd usually want your views to still render even though a translation is missing. -In other contexts you might want to change this behaviour, though. E.g. the default exception handling does not allow to catch missing translations during automated tests easily. For this purpose a different exception handler can be specified. The specified exception handler must be a method on the I18n module: +In other contexts you might want to change this behaviour, though. E.g. the default exception handling does not allow to catch missing translations during automated tests easily. For this purpose a different exception handler can be specified. The specified exception handler must be a method on the I18n module or a class with +#call+ method: <ruby> module I18n - def self.just_raise_that_exception(*args) - raise args.first + class JustRaiseExceptionHandler < ExceptionHandler + def call(exception, locale, key, options) + if exception.is_a?(MissingTranslation) + raise exception.to_exception + else + super + end + end end end -I18n.exception_handler = :just_raise_that_exception +I18n.exception_handler = I18n::JustRaiseExceptionHandler.new </ruby> -This would re-raise all caught exceptions including +MissingTranslationData+. +This would re-raise only the +MissingTranslationData+ exception, passing all other input to the default exception handler. + +However, if you are using +I18n::Backend::Pluralization+ this handler will also raise +I18n::MissingTranslationData: translation missing: en.i18n.plural.rule+ exception that should normally be ignored to fall back to the default pluralization rule for English locale. To avoid this you may use additional check for translation key: + +<ruby> +if exception.is_a?(MissingTranslation) && key.to_s != 'i18n.plural.rule' + raise exception.to_exception +else + super +end +</ruby> Another example where the default behaviour is less desirable is the Rails TranslationHelper which provides the method +#t+ (as well as +#translate+). When a +MissingTranslationData+ exception occurs in this context, the helper wraps the message into a span with the CSS class +translation_missing+. diff --git a/guides/source/index.html.erb b/guides/source/index.html.erb index 5439459b42..74805b2754 100644 --- a/guides/source/index.html.erb +++ b/guides/source/index.html.erb @@ -13,7 +13,7 @@ Ruby on Rails Guides and <%= link_to 'Free Kindle Reading Apps', 'http://www.amazon.com/gp/kindle/kcp' %> for the iPad, iPhone, Mac, Android, etc. Download them from <%= link_to 'here', @mobi %>. </dd> - <dd class="work-in-progress">Guides marked with this icon are currently being worked on. While they might still be useful to you, they may contain incomplete information and even errors. You can help by reviewing them and posting your comments and corrections to the author.</dd> + <dd class="work-in-progress">Guides marked with this icon are currently being worked on and will not be available in the Guides Index menu. While still useful, they may contain incomplete information and even errors. You can help by reviewing them and posting your comments and corrections.</dd> </dl> </div> <% end %> diff --git a/guides/source/initialization.textile b/guides/source/initialization.textile index 69e5c1edcc..155a439e64 100644 --- a/guides/source/initialization.textile +++ b/guides/source/initialization.textile @@ -137,8 +137,6 @@ h4. +config/boot.rb+ +config/boot.rb+ contains this: <ruby> -require 'rubygems' - # Set up gems listed in the Gemfile. gemfile = File.expand_path('../../Gemfile', __FILE__) begin diff --git a/guides/source/layout.html.erb b/guides/source/layout.html.erb index 35b6fc7014..0a8daf7ae5 100644 --- a/guides/source/layout.html.erb +++ b/guides/source/layout.html.erb @@ -14,6 +14,8 @@ <link rel="stylesheet" type="text/css" href="stylesheets/syntaxhighlighter/shThemeRailsGuides.css" /> <link rel="stylesheet" type="text/css" href="stylesheets/fixes.css" /> + +<link href="images/favicon.ico" rel="shortcut icon" type="image/x-icon" /> </head> <body class="guide"> <% if @edge %> diff --git a/guides/source/layouts_and_rendering.textile b/guides/source/layouts_and_rendering.textile index 4b4f9f3745..b0a87a5981 100644 --- a/guides/source/layouts_and_rendering.textile +++ b/guides/source/layouts_and_rendering.textile @@ -78,16 +78,16 @@ If we want to display the properties of all the books in our view, we can do so <tr> <td><%= book.title %></td> <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 "Show", book %></td> + <td><%= link_to "Edit", edit_book_path(book) %></td> + <td><%= link_to "Remove", book, :confirm => "Are you sure?", :method => :delete %></td> </tr> <% end %> </table> <br /> -<%= link_to 'New book', new_book_path %> +<%= link_to "New book", new_book_path %> </ruby> NOTE: The actual rendering is done by subclasses of +ActionView::TemplateHandlers+. This guide does not dig into that process, but it's important to know that the file extension on your view controls the choice of template handler. Beginning with Rails 2, the standard extensions are +.erb+ for ERB (HTML with embedded Ruby), and +.builder+ for Builder (XML generator). @@ -177,13 +177,13 @@ h5. Rendering an Action's Template from Another Controller What if you want to render a template from an entirely different controller from the one that contains the action code? You can also do that with +render+, which accepts the full path (relative to +app/views+) of the template to render. For example, if you're running code in an +AdminProductsController+ that lives in +app/controllers/admin+, you can render the results of an action to a template in +app/views/products+ this way: <ruby> -render 'products/show' +render "products/show" </ruby> Rails knows that this view belongs to a different controller because of the embedded slash character in the string. If you want to be explicit, you can use the +:template+ option (which was required on Rails 2.2 and earlier): <ruby> -render :template => 'products/show' +render :template => "products/show" </ruby> h5. Rendering an Arbitrary File @@ -216,18 +216,18 @@ In fact, in the BooksController class, inside of the update action where we want <ruby> render :edit render :action => :edit -render 'edit' -render 'edit.html.erb' -render :action => 'edit' -render :action => 'edit.html.erb' -render 'books/edit' -render 'books/edit.html.erb' -render :template => 'books/edit' -render :template => 'books/edit.html.erb' -render '/path/to/rails/app/views/books/edit' -render '/path/to/rails/app/views/books/edit.html.erb' -render :file => '/path/to/rails/app/views/books/edit' -render :file => '/path/to/rails/app/views/books/edit.html.erb' +render "edit" +render "edit.html.erb" +render :action => "edit" +render :action => "edit.html.erb" +render "books/edit" +render "books/edit.html.erb" +render :template => "books/edit" +render :template => "books/edit.html.erb" +render "/path/to/rails/app/views/books/edit" +render "/path/to/rails/app/views/books/edit.html.erb" +render :file => "/path/to/rails/app/views/books/edit" +render :file => "/path/to/rails/app/views/books/edit.html.erb" </ruby> Which one you use is really a matter of style and convention, but the rule of thumb is to use the simplest one that makes sense for the code you are writing. @@ -306,7 +306,7 @@ h6. The +:content_type+ Option By default, Rails will serve the results of a rendering operation with the MIME content-type of +text/html+ (or +application/json+ if you use the +:json+ option, or +application/xml+ for the +:xml+ option.). There are times when you might like to change this, and you can do so by setting the +:content_type+ option: <ruby> -render :file => filename, :content_type => 'application/rss' +render :file => filename, :content_type => "application/rss" </ruby> h6. The +:layout+ Option @@ -316,7 +316,7 @@ With most of the options to +render+, the rendered content is displayed as part You can use the +:layout+ option to tell Rails to use a specific file as the layout for the current action: <ruby> -render :layout => 'special_layout' +render :layout => "special_layout" </ruby> You can also tell Rails to render with no layout at all: @@ -378,7 +378,7 @@ You can use a symbol to defer the choice of layout until a request is processed: <ruby> class ProductsController < ApplicationController - layout :products_layout + layout "products_layout" def show @product = Product.find(params[:id]) @@ -398,7 +398,7 @@ You can even use an inline method, such as a Proc, to determine the layout. For <ruby> class ProductsController < ApplicationController - layout Proc.new { |controller| controller.request.xhr? ? 'popup' : 'application' } + layout Proc.new { |controller| controller.request.xhr? ? "popup" : "application" } end </ruby> @@ -445,7 +445,7 @@ end <ruby> class OldPostsController < SpecialPostsController - layout nil + layout false def show @post = Post.find(params[:id]) @@ -583,7 +583,7 @@ def show @book = Book.find_by_id(params[:id]) if @book.nil? @books = Book.all - render "index", :alert => 'Your book was not found!' + render "index", :alert => "Your book was not found!" end end </ruby> @@ -686,7 +686,7 @@ You can specify a full path relative to the document root, or a URL, if you pref Rails will then output a +script+ tag such as this: <html> -<script src='/assets/main.js' type="text/javascript"></script> +<script src='/assets/main.js'></script> </html> The request to this asset is then served by the Sprockets gem. @@ -718,8 +718,8 @@ If the application does not use the asset pipeline, the +:defaults+ option loads Outputting +script+ tags such as this: <html> -<script src="/javascripts/jquery.js" type="text/javascript"></script> -<script src="/javascripts/jquery_ujs.js" type="text/javascript"></script> +<script src="/javascripts/jquery.js"></script> +<script src="/javascripts/jquery_ujs.js"></script> </html> These two files for jQuery, +jquery.js+ and +jquery_ujs.js+ must be placed inside +public/javascripts+ if the application doesn't use the asset pipeline. These files can be downloaded from the "jquery-rails repository on GitHub":https://github.com/indirect/jquery-rails/tree/master/vendor/assets/javascripts @@ -770,7 +770,7 @@ By default, the combined file will be delivered as +javascripts/all.js+. You can <erb> <%= javascript_include_tag "main", "columns", - :cache => 'cache/main/display' %> + :cache => "cache/main/display" %> </erb> You can even use dynamic paths such as +cache/#{current_site}/main/display+. @@ -805,7 +805,7 @@ To include +http://example.com/main.css+: <%= stylesheet_link_tag "http://example.com/main.css" %> </erb> -By default, the +stylesheet_link_tag+ creates links with +media="screen" rel="stylesheet" type="text/css"+. You can override any of these defaults by specifying an appropriate option (+:media+, +:rel+, or +:type+): +By default, the +stylesheet_link_tag+ creates links with +media="screen" rel="stylesheet"+. You can override any of these defaults by specifying an appropriate option (+:media+, +:rel+): <erb> <%= stylesheet_link_tag "main_print", :media => "print" %> @@ -833,7 +833,7 @@ By default, the combined file will be delivered as +stylesheets/all.css+. You ca <erb> <%= stylesheet_link_tag "main", "columns", - :cache => 'cache/main/display' %> + :cache => "cache/main/display" %> </erb> You can even use dynamic paths such as +cache/#{current_site}/main/display+. @@ -860,12 +860,6 @@ You can supply a hash of additional HTML options: <%= image_tag "icons/delete.gif", {:height => 45} %> </erb> -You can also supply an alternate image to show on mouseover: - -<erb> -<%= image_tag "home.gif", :onmouseover => "menu/home_highlight.gif" %> -</erb> - You can supply alternate text for the image which will be used if the user has images turned off in their browser. If you do not specify an alt text explicitly, it defaults to the file name of the file, capitalized and with no extension. For example, these two image tags would return the same code: <erb> @@ -884,7 +878,7 @@ In addition to the above special tags, you can supply a final hash of standard H <erb> <%= image_tag "home.gif", :alt => "Go Home", :id => "HomeImage", - :class => 'nav_bar' %> + :class => "nav_bar" %> </erb> h5. Linking to Videos with the +video_tag+ @@ -905,7 +899,7 @@ Like an +image_tag+ you can supply a path, either absolute, or relative to the + The video tag also supports all of the +<video>+ HTML options through the HTML options hash, including: -* +:poster => 'image_name.png'+, provides an image to put in place of the video before it starts playing. +* +:poster => "image_name.png"+, provides an image to put in place of the video before it starts playing. * +:autoplay => true+, starts playing the video on page load. * +:loop => true+, loops the video once it gets to the end. * +:controls => true+, provides browser supplied controls for the user to interact with the video. @@ -1134,13 +1128,6 @@ In Rails 3.0, there is also a shorthand for this. Assuming +@products+ is a coll Rails determines the name of the partial to use by looking at the model name in the collection. In fact, you can even create a heterogeneous collection and render it this way, and Rails will choose the proper partial for each member of the collection: -In the event that the collection is empty, +render+ will return nil, so it should be fairly simple to provide alternative content. - -<erb> -<h1>Products</h1> -<%= render(@products) || 'There are no products available.' %> -</erb> - * +index.html.erb+ <erb> @@ -1162,6 +1149,13 @@ In the event that the collection is empty, +render+ will return nil, so it shoul In this case, Rails will use the customer or employee partials as appropriate for each member of the collection. +In the event that the collection is empty, +render+ will return nil, so it should be fairly simple to provide alternative content. + +<erb> +<h1>Products</h1> +<%= render(@products) || "There are no products available." %> +</erb> + h5. Local Variables To use a custom local variable name within the partial, specify the +:as+ option in the call to the partial: @@ -1175,7 +1169,7 @@ With this change, you can access an instance of the +@products+ collection as th You can also pass in arbitrary local variables to any partial you are rendering with the +:locals => {}+ option: <erb> -<%= render :partial => 'products', :collection => @products, +<%= render :partial => "products", :collection => @products, :as => :item, :locals => {:title => "Products Page"} %> </erb> @@ -1193,6 +1187,16 @@ 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 + +When rendering collections it is also possible to use the +:layout+ option: + +<erb> +<%= render :partial => "product", :collection => @products, :layout => "special_layout" %> +</erb> + +The layout will be rendered together with the partial for each item in the collection. The current object and object_counter variables will be available in the layout as well, the same way they do within the partial. + h4. Using Nested Layouts You may find that your application requires a layout that differs slightly from your regular application layout to support one particular controller. Rather than repeating the main layout and editing it, you can accomplish this by using nested layouts (sometimes called sub-templates). Here's an example: @@ -1204,9 +1208,9 @@ Suppose you have the following +ApplicationController+ layout: <erb> <html> <head> - <title><%= @page_title or 'Page Title' %></title> - <%= stylesheet_link_tag 'layout' %> - <style type="text/css"><%= yield :stylesheets %></style> + <title><%= @page_title or "Page Title" %></title> + <%= stylesheet_link_tag "layout" %> + <style><%= yield :stylesheets %></style> </head> <body> <div id="top_menu">Top menu items here</div> @@ -1229,7 +1233,7 @@ On pages generated by +NewsController+, you want to hide the top menu and add a <div id="right_menu">Right menu items here</div> <%= content_for?(:news_content) ? yield(:news_content) : yield %> <% end %> -<%= render :template => 'layouts/application' %> +<%= render :template => "layouts/application" %> </erb> That's it. The News views will use the new layout, hiding the top menu and adding a new right menu inside the "content" div. diff --git a/guides/source/migrations.textile b/guides/source/migrations.textile index c11f8e221b..52dba76e68 100644 --- a/guides/source/migrations.textile +++ b/guides/source/migrations.textile @@ -51,7 +51,7 @@ end This migration adds a table called +products+ with a string column called +name+ and a text column called +description+. A primary key column called +id+ will -also be added, however since this is the default we do not need to ask for this. +also be added, however since this is the default we do not need to explicitly specify it. The timestamp columns +created_at+ and +updated_at+ which Active Record populates automatically will also be added. Reversing this migration is as simple as dropping the table. @@ -65,7 +65,7 @@ class AddReceiveNewsletterToUsers < ActiveRecord::Migration change_table :users do |t| t.boolean :receive_newsletter, :default => false end - User.update_all ["receive_newsletter = ?", true] + User.update_all :receive_newsletter => true end def down @@ -82,6 +82,8 @@ 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 + 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 tables). The migration knows how to migrate your database and reverse it when @@ -475,7 +477,16 @@ end </ruby> will add an +attachment_id+ column and a string +attachment_type+ column with -a default value of 'Photo'. +a default value of 'Photo'. +references+ also allows you to define an +index directly, instead of using +add_index+ after the +create_table+ call: + +<ruby> +create_table :products do |t| + t.references :category, :index => true +end +</ruby> + +will create an index identical to calling `add_index :products, :category_id`. NOTE: The +references+ helper does not actually create foreign key constraints for you. You will need to use +execute+ or a plugin that adds "foreign key @@ -497,7 +508,7 @@ and h4. Using the +change+ Method The +change+ method removes the need to write both +up+ and +down+ methods in -those cases that Rails know how to revert the changes automatically. Currently, +those cases that Rails knows how to revert the changes automatically. Currently, the +change+ method supports only these migration definitions: * +add_column+ @@ -635,10 +646,9 @@ example, $ rake db:migrate:up VERSION=20080906120000 </shell> -will run the +up+ method from the 20080906120000 migration. These tasks still -check whether the migration has already run, so for example +db:migrate:up -VERSION=20080906120000+ will do nothing if Active Record believes that -20080906120000 has already been run. +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 @@ -727,9 +737,7 @@ column. class AddFlagToProduct < ActiveRecord::Migration def change add_column :products, :flag, :boolean - Product.all.each do |product| - product.update_attributes!(:flag => 'false') - end + Product.update_all :flag => false end end </ruby> @@ -752,9 +760,7 @@ column. class AddFuzzToProduct < ActiveRecord::Migration def change add_column :products, :fuzz, :string - Product.all.each do |product| - product.update_attributes! :fuzz => 'fuzzy' - end + Product.update_all :fuzz => 'fuzzy' end end </ruby> @@ -771,7 +777,7 @@ Both migrations work for Alice. Bob comes back from vacation and: -# Updates the source - which contains both migrations and the latests version of +# Updates the source - which contains both migrations and the latest version of the Product model. # Runs outstanding migrations with +rake db:migrate+, which includes the one that updates the +Product+ model. @@ -804,11 +810,9 @@ class AddFlagToProduct < ActiveRecord::Migration end def change - add_column :products, :flag, :integer + add_column :products, :flag, :boolean Product.reset_column_information - Product.all.each do |product| - product.update_attributes!(:flag => false) - end + Product.update_all :flag => false end end </ruby> @@ -823,9 +827,7 @@ class AddFuzzToProduct < ActiveRecord::Migration def change add_column :products, :fuzz, :string Product.reset_column_information - Product.all.each do |product| - product.update_attributes!(:fuzz => 'fuzzy') - end + Product.update_all :fuzz => 'fuzzy' end end </ruby> diff --git a/guides/source/plugins.textile b/guides/source/plugins.textile index 97b4eca779..95e38db483 100644 --- a/guides/source/plugins.textile +++ b/guides/source/plugins.textile @@ -25,16 +25,14 @@ endprologue. h3. Setup -Before you continue, take a moment to decide if your new plugin will be potentially shared across different Rails applications. +_"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. -* If your plugin is specific to your application, your new plugin will be a _vendored plugin_. -* If you think your plugin may be used across applications, build it as a _gemified plugin_. +Currently, Rails plugins are built as gems, _gemified plugins_. They can be shared accross +different rails applications using RubyGems and Bundler if desired. h4. Generate a gemified plugin. -Writing your Rails plugin as a gem, rather than as a vendored plugin, - lets you share your plugin across different rails applications using - RubyGems and Bundler. Rails 3.1 ships with a +rails plugin new+ command which creates a skeleton for developing any kind of Rails extension with the ability diff --git a/guides/source/rails_on_rack.textile b/guides/source/rails_on_rack.textile index 9526526bc7..ff862273fd 100644 --- a/guides/source/rails_on_rack.textile +++ b/guides/source/rails_on_rack.textile @@ -91,13 +91,15 @@ For a freshly generated Rails application, this might produce something like: <ruby> use ActionDispatch::Static use Rack::Lock -use ActiveSupport::Cache::Strategy::LocalCache +use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x000000029a0838> use Rack::Runtime +use Rack::MethodOverride +use ActionDispatch::RequestId use Rails::Rack::Logger use ActionDispatch::ShowExceptions use ActionDispatch::DebugExceptions use ActionDispatch::RemoteIp -use Rack::Sendfile +use ActionDispatch::Reloader use ActionDispatch::Callbacks use ActiveRecord::ConnectionAdapters::ConnectionManagement use ActiveRecord::QueryCache @@ -105,8 +107,9 @@ use ActionDispatch::Cookies use ActionDispatch::Session::CookieStore use ActionDispatch::Flash use ActionDispatch::ParamsParser -use Rack::MethodOverride use ActionDispatch::Head +use Rack::ConditionalGet +use Rack::ETag use ActionDispatch::BestStandardsSupport run Blog::Application.routes </ruby> @@ -145,62 +148,104 @@ You can swap an existing middleware in the middleware stack using +config.middle <ruby> # config/application.rb -# Replace ActionController::Failsafe with Lifo::Failsafe -config.middleware.swap ActionController::Failsafe, Lifo::Failsafe +# Replace ActionDispatch::ShowExceptions with Lifo::ShowExceptions +config.middleware.swap ActionDispatch::ShowExceptions, Lifo::ShowExceptions </ruby> h5. Middleware Stack is an Array The middleware stack behaves just like a normal +Array+. You can use any +Array+ methods to insert, reorder, or remove items from the stack. Methods described in the section above are just convenience methods. -For example, the following removes the middleware matching the supplied class name: +Append following lines to your application configuration: <ruby> -config.middleware.delete(middleware) +# config/application.rb +config.middleware.delete "Rack::Lock" </ruby> +And now if you inspect the middleware stack, you'll find that +Rack::Lock+ will not be part of it. + +<shell> +$ rake middleware +(in /Users/lifo/Rails/blog) +use ActionDispatch::Static +use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x00000001c304c8> +use Rack::Runtime +... +run Myapp::Application.routes +</shell> + h4. Internal Middleware Stack -Much of Action Controller's functionality is implemented as Middlewares. The following table explains the purpose of each of them: +Much of Action Controller's functionality is implemented as Middlewares. The following list explains the purpose of each of them: -|_.Middleware|_.Purpose| -|+Rack::Lock+|Sets <tt>env["rack.multithread"]</tt> flag to +true+ and wraps the application within a Mutex.| -|+ActionController::Failsafe+|Returns HTTP Status +500+ to the client if an exception gets raised while dispatching.| -|+ActiveRecord::QueryCache+|Enables the Active Record query cache.| -|+ActionDispatch::Session::CookieStore+|Uses the cookie based session store.| -|+ActionDispatch::Session::CacheStore+|Uses the Rails cache based session store.| -|+ActionDispatch::Session::MemCacheStore+|Uses the memcached based session store.| -|+ActiveRecord::SessionStore+|Uses the database based session store.| -|+Rack::MethodOverride+|Sets HTTP method based on +_method+ parameter or <tt>env["HTTP_X_HTTP_METHOD_OVERRIDE"]</tt>.| -|+Rack::Head+|Discards the response body if the client sends a +HEAD+ request.| + *+ActionDispatch::Static+* +* Used to serve static assets. Disabled if <tt>config.serve_static_assets</tt> is true. -TIP: It's possible to use any of the above middlewares in your custom Rack stack. + *+Rack::Lock+* +* Sets <tt>env["rack.multithread"]</tt> flag to +true+ and wraps the application within a Mutex. -h4. Customizing Internal Middleware Stack + *+ActiveSupport::Cache::Strategy::LocalCache::Middleware+* +* Used for memory caching. This cache is not thread safe. -It's possible to replace the entire middleware stack with a custom stack using <tt>ActionController::Dispatcher.middleware=</tt>. + *+Rack::Runtime+* +* Sets an X-Runtime header, containing the time (in seconds) taken to execute the request. -Put the following in an initializer: + *+Rack::MethodOverride+* +* Allows the method to be overridden if <tt>params[:_method]</tt> is set. This is the middleware which supports the PUT and DELETE HTTP method types. -<ruby> -# config/initializers/stack.rb -ActionController::Dispatcher.middleware = ActionController::MiddlewareStack.new do |m| - m.use ActionController::Failsafe - m.use ActiveRecord::QueryCache - m.use Rack::Head -end -</ruby> + *+ActionDispatch::RequestId+* +* Makes a unique +X-Request-Id+ header available to the response and enables the <tt>ActionDispatch::Request#uuid</tt> method. -And now inspecting the middleware stack: + *+Rails::Rack::Logger+* +* Notifies the logs that the request has began. After request is complete, flushes all the logs. -<shell> -$ rake middleware -(in /Users/lifo/Rails/blog) -use ActionController::Failsafe -use ActiveRecord::QueryCache -use Rack::Head -run ActionController::Dispatcher.new -</shell> + *+ActionDispatch::ShowExceptions+* +* Rescues any exception returned by the application and calls an exceptions app that will wrap it in a format for the end user. + + *+ActionDispatch::DebugExceptions+* +* Responsible for logging exceptions and showing a debugging page in case the request is local. + + *+ActionDispatch::RemoteIp+* +* Checks for IP spoofing attacks. + + *+ActionDispatch::Reloader+* +* Provides prepare and cleanup callbacks, intended to assist with code reloading during development. + + *+ActionDispatch::Callbacks+* +* Runs the prepare callbacks before serving the request. + + *+ActiveRecord::ConnectionAdapters::ConnectionManagement+* +* Cleans active connections after each request, unless the <tt>rack.test</tt> key in the request environment is set to +true+. + + *+ActiveRecord::QueryCache+* +* Enables the Active Record query cache. + + *+ActionDispatch::Cookies+* +* Sets cookies for the request. + + *+ActionDispatch::Session::CookieStore+* +* Responsible for storing the session in cookies. + + *+ActionDispatch::Flash+* +* Sets up the flash keys. Only available if <tt>config.action_controller.session_store</tt> is set to a value. + + *+ActionDispatch::ParamsParser+* +* Parses out parameters from the request into <tt>params</tt>. + + *+ActionDispatch::Head+* +* Converts HEAD requests to +GET+ requests and serves them as so. + + *+Rack::ConditionalGet+* +* Adds support for "Conditional +GET+" so that server responds with nothing if page wasn't changed. + + *+Rack::ETag+* +* Adds ETag header on all String bodies. ETags are used to validate cache. + + *+ActionDispatch::BestStandardsSupport+* +* Enables “best standards support” so that IE8 renders some elements correctly. + +TIP: It's possible to use any of the above middlewares in your custom Rack stack. h4. Using Rack Builder diff --git a/guides/source/routing.textile b/guides/source/routing.textile index e93b1280e0..4a50edbb15 100644 --- a/guides/source/routing.textile +++ b/guides/source/routing.textile @@ -25,7 +25,7 @@ GET /patients/17 it asks the router to match it to a controller action. If the first matching route is <ruby> -match "/patients/:id" => "patients#show" +get "/patients/:id" => "patients#show" </ruby> the request is dispatched to the +patients+ controller's +show+ action with <tt>{ :id => "17" }</tt> in +params+. @@ -121,7 +121,7 @@ h4. Singular Resources Sometimes, you have a resource that clients always look up without referencing an ID. For example, you would like +/profile+ to always show the profile of the currently logged in user. In this case, you can use a singular resource to map +/profile+ (rather than +/profile/:id+) to the +show+ action. <ruby> -match "profile" => "users#show" +get "profile" => "users#show" </ruby> This resourceful route @@ -374,7 +374,7 @@ h4. Bound Parameters When you set up a regular route, you supply a series of symbols that Rails maps to parts of an incoming HTTP request. Two of these symbols are special: +:controller+ maps to the name of a controller in your application, and +:action+ maps to the name of an action within that controller. For example, consider one of the default Rails routes: <ruby> -match ':controller(/:action(/:id))' +get ':controller(/:action(/:id))' </ruby> If an incoming request of +/photos/show/1+ is processed by this route (because it hasn't matched any previous route in the file), then the result will be to invoke the +show+ action of the +PhotosController+, and to make the final parameter +"1"+ available as +params[:id]+. This route will also route the incoming request of +/photos+ to +PhotosController#index+, since +:action+ and +:id+ are optional parameters, denoted by parentheses. @@ -384,7 +384,7 @@ h4. Dynamic Segments You can set up as many dynamic segments within a regular route as you like. Anything other than +:controller+ or +:action+ will be available to the action as part of +params+. If you set up this route: <ruby> -match ':controller/:action/:id/:user_id' +get ':controller/:action/:id/:user_id' </ruby> An incoming path of +/photos/show/1/2+ will be dispatched to the +show+ action of the +PhotosController+. +params[:id]+ will be +"1"+, and +params[:user_id]+ will be +"2"+. @@ -392,7 +392,7 @@ An incoming path of +/photos/show/1/2+ will be dispatched to the +show+ action o NOTE: You can't use +:namespace+ or +:module+ with a +:controller+ path segment. If you need to do this then use a constraint on :controller that matches the namespace you require. e.g: <ruby> -match ':controller(/:action(/:id))', :controller => /admin\/[^\/]+/ +get ':controller(/:action(/:id))', :controller => /admin\/[^\/]+/ </ruby> TIP: By default dynamic segments don't accept dots - this is because the dot is used as a separator for formatted routes. If you need to use a dot within a dynamic segment add a constraint which overrides this - for example +:id+ => /[^\/]+/ allows anything except a slash. @@ -402,7 +402,7 @@ h4. Static Segments You can specify static segments when creating a route: <ruby> -match ':controller/:action/:id/with_user/:user_id' +get ':controller/:action/:id/with_user/:user_id' </ruby> This route would respond to paths such as +/photos/show/1/with_user/2+. In this case, +params+ would be <tt>{ :controller => "photos", :action => "show", :id => "1", :user_id => "2" }</tt>. @@ -412,7 +412,7 @@ h4. The Query String The +params+ will also include any parameters from the query string. For example, with this route: <ruby> -match ':controller/:action/:id' +get ':controller/:action/:id' </ruby> An incoming path of +/photos/show/1?user_id=2+ will be dispatched to the +show+ action of the +Photos+ controller. +params+ will be <tt>{ :controller => "photos", :action => "show", :id => "1", :user_id => "2" }</tt>. @@ -422,7 +422,7 @@ h4. Defining Defaults You do not need to explicitly use the +:controller+ and +:action+ symbols within a route. You can supply them as defaults: <ruby> -match 'photos/:id' => 'photos#show' +get 'photos/:id' => 'photos#show' </ruby> With this route, Rails will match an incoming path of +/photos/12+ to the +show+ action of +PhotosController+. @@ -430,7 +430,7 @@ With this route, Rails will match an incoming path of +/photos/12+ to the +show+ You can also define other defaults in a route by supplying a hash for the +:defaults+ option. This even applies to parameters that you do not specify as dynamic segments. For example: <ruby> -match 'photos/:id' => 'photos#show', :defaults => { :format => 'jpg' } +get 'photos/:id' => 'photos#show', :defaults => { :format => 'jpg' } </ruby> Rails would match +photos/12+ to the +show+ action of +PhotosController+, and set +params[:format]+ to +"jpg"+. @@ -440,49 +440,45 @@ h4. Naming Routes You can specify a name for any route using the +:as+ option. <ruby> -match 'exit' => 'sessions#destroy', :as => :logout +get 'exit' => 'sessions#destroy', :as => :logout </ruby> This will create +logout_path+ and +logout_url+ as named helpers in your application. Calling +logout_path+ will return +/exit+ h4. HTTP Verb Constraints -You can use the +:via+ option to constrain the request to one or more HTTP methods: +In general, you should use the +get+, +post+, +put+ and +delete+ methods to constrain a route to a particular verb. You can use the +match+ method with the +:via+ option to match multiple verbs at once: <ruby> -match 'photos/show' => 'photos#show', :via => :get +match 'photos' => 'photos#show', :via => [:get, :post] </ruby> -There is a shorthand version of this as well: +You can match all verbs to a particular route using +:via => :all+: <ruby> -get 'photos/show' +match 'photos' => 'photos#show', :via => :all </ruby> -You can also permit more than one verb to a single route: - -<ruby> -match 'photos/show' => 'photos#show', :via => [:get, :post] -</ruby> +You should avoid routing all verbs to an action unless you have a good reason to, as routing both +GET+ requests and +POST+ requests to a single action has security implications. h4. Segment Constraints You can use the +:constraints+ option to enforce a format for a dynamic segment: <ruby> -match 'photos/:id' => 'photos#show', :constraints => { :id => /[A-Z]\d{5}/ } +get 'photos/:id' => 'photos#show', :constraints => { :id => /[A-Z]\d{5}/ } </ruby> This route would match paths such as +/photos/A12345+. You can more succinctly express the same route this way: <ruby> -match 'photos/:id' => 'photos#show', :id => /[A-Z]\d{5}/ +get 'photos/:id' => 'photos#show', :id => /[A-Z]\d{5}/ </ruby> +:constraints+ takes regular expressions with the restriction that regexp anchors can't be used. For example, the following route will not work: <ruby> -match '/:id' => 'posts#show', :constraints => {:id => /^\d/} +get '/:id' => 'posts#show', :constraints => {:id => /^\d/} </ruby> However, note that you don't need to use anchors because all routes are anchored at the start. @@ -490,8 +486,8 @@ However, note that you don't need to use anchors because all routes are anchored For example, the following routes would allow for +posts+ with +to_param+ values like +1-hello-world+ that always begin with a number and +users+ with +to_param+ values like +david+ that never begin with a number to share the root namespace: <ruby> -match '/:id' => 'posts#show', :constraints => { :id => /\d.+/ } -match '/:username' => 'users#show' +get '/:id' => 'posts#show', :constraints => { :id => /\d.+/ } +get '/:username' => 'users#show' </ruby> h4. Request-Based Constraints @@ -501,7 +497,7 @@ You can also constrain a route based on any method on the <a href="action_contro You specify a request-based constraint the same way that you specify a segment constraint: <ruby> -match "photos", :constraints => {:subdomain => "admin"} +get "photos", :constraints => {:subdomain => "admin"} </ruby> You can also specify constraints in a block form: @@ -530,17 +526,28 @@ class BlacklistConstraint end TwitterClone::Application.routes.draw do - match "*path" => "blacklist#index", + get "*path" => "blacklist#index", :constraints => BlacklistConstraint.new end </ruby> +You can also specify constraints as a lambda: + +<ruby> +TwitterClone::Application.routes.draw do + get "*path" => "blacklist#index", + :constraints => lambda { |request| Blacklist.retrieve_ips.include?(request.remote_ip) } +end +</ruby> + +Both the +matches?+ method and the lambda gets the +request+ object as an argument. + h4. Route Globbing Route globbing is a way to specify that a particular parameter should be matched to all the remaining parts of a route. For example <ruby> -match 'photos/*other' => 'photos#unknown' +get 'photos/*other' => 'photos#unknown' </ruby> This route would match +photos/12+ or +/photos/long/path/to/12+, setting +params[:other]+ to +"12"+ or +"long/path/to/12"+. @@ -548,7 +555,7 @@ This route would match +photos/12+ or +/photos/long/path/to/12+, setting +params Wildcard segments can occur anywhere in a route. For example, <ruby> -match 'books/*section/:title' => 'books#show' +get 'books/*section/:title' => 'books#show' </ruby> would match +books/some/section/last-words-a-memoir+ with +params[:section]+ equals +"some/section"+, and +params[:title]+ equals +"last-words-a-memoir"+. @@ -556,7 +563,7 @@ would match +books/some/section/last-words-a-memoir+ with +params[:section]+ equ Technically a route can have even more than one wildcard segment. The matcher assigns segments to parameters in an intuitive way. For example, <ruby> -match '*a/foo/*b' => 'test#index' +get '*a/foo/*b' => 'test#index' </ruby> would match +zoo/woo/foo/bar/baz+ with +params[:a]+ equals +"zoo/woo"+, and +params[:b]+ equals +"bar/baz"+. @@ -564,19 +571,19 @@ would match +zoo/woo/foo/bar/baz+ with +params[:a]+ equals +"zoo/woo"+, and +par NOTE: Starting from Rails 3.1, wildcard routes will always match the optional format segment by default. For example if you have this route: <ruby> -match '*pages' => 'pages#show' +get '*pages' => 'pages#show' </ruby> NOTE: By requesting +"/foo/bar.json"+, your +params[:pages]+ will be equals to +"foo/bar"+ with the request format of JSON. If you want the old 3.0.x behavior back, you could supply +:format => false+ like this: <ruby> -match '*pages' => 'pages#show', :format => false +get '*pages' => 'pages#show', :format => false </ruby> NOTE: If you want to make the format segment mandatory, so it cannot be omitted, you can supply +:format => true+ like this: <ruby> -match '*pages' => 'pages#show', :format => true +get '*pages' => 'pages#show', :format => true </ruby> h4. Redirection @@ -584,20 +591,20 @@ h4. Redirection You can redirect any path to another path using the +redirect+ helper in your router: <ruby> -match "/stories" => redirect("/posts") +get "/stories" => redirect("/posts") </ruby> You can also reuse dynamic segments from the match in the path to redirect to: <ruby> -match "/stories/:name" => redirect("/posts/%{name}") +get "/stories/:name" => redirect("/posts/%{name}") </ruby> You can also provide a block to redirect, which receives the params and (optionally) the request object: <ruby> -match "/stories/:name" => redirect {|params| "/posts/#{params[:name].pluralize}" } -match "/stories" => redirect {|p, req| "/posts/#{req.subdomain}" } +get "/stories/:name" => redirect {|params| "/posts/#{params[:name].pluralize}" } +get "/stories" => redirect {|p, req| "/posts/#{req.subdomain}" } </ruby> Please note that this redirection is a 301 "Moved Permanently" redirect. Keep in mind that some web browsers or proxy servers will cache this type of redirect, making the old page inaccessible. @@ -609,10 +616,10 @@ h4. Routing to Rack Applications Instead of a String, like +"posts#index"+, which corresponds to the +index+ action in the +PostsController+, you can specify any <a href="rails_on_rack.html">Rack application</a> as the endpoint for a matcher. <ruby> -match "/application.js" => Sprockets +match "/application.js" => Sprockets, :via => :all </ruby> -As long as +Sprockets+ responds to +call+ and returns a <tt>[status, headers, body]</tt>, the router won't know the difference between the Rack application and an action. +As long as +Sprockets+ responds to +call+ and returns a <tt>[status, headers, body]</tt>, the router won't know the difference between the Rack application and an action. This is an appropriate use of +:via => :all+, as you will want to allow your Rack application to handle all verbs as it considers appropriate. NOTE: For the curious, +"posts#index"+ actually expands out to +PostsController.action(:index)+, which returns a valid Rack application. @@ -627,6 +634,8 @@ root 'pages#main' # shortcut for the above You should put the +root+ route at the top of the file, because it is the most popular route and should be matched first. You also need to delete the +public/index.html+ file for the root route to take effect. +NOTE: The +root+ route only routes +GET+ requests to the action. + h3. Customizing Resourceful Routes While the default routes and helpers generated by +resources :posts+ will usually serve you well, you may want to customize them in some way. Rails allows you to customize virtually any generic part of the resourceful helpers. @@ -820,6 +829,24 @@ end This will create routing helpers such as +magazine_periodical_ads_url+ and +edit_magazine_periodical_ad_path+. +h3. Breaking Up a Large Route File + +If you have a large route file that you would like to break up into multiple files, you can use the +#draw+ method in your router: + +<ruby> +draw :admin +</ruby> + +Then, create a file called +config/routes/admin.rb+. Name the file the same as the symbol passed to the +draw+ method. You can then use the normal routing DSL inside that file: + +<ruby> +# in config/routes/admin.rb + +namespace :admin do + resources :posts +end +</ruby> + h3. Inspecting and Testing Routes Rails offers facilities for inspecting and testing your routes. diff --git a/guides/source/security.textile b/guides/source/security.textile index 747a4d6791..ac55d60368 100644 --- a/guides/source/security.textile +++ b/guides/source/security.textile @@ -1,7 +1,6 @@ h2. Ruby On Rails Security Guide -This manual describes common security problems in web applications and how to avoid them with Rails. If you have any questions or suggestions, please -mail me, Heiko Webers, at 42 {_et_} rorsecurity.info. After reading it, you should be familiar with: +This manual describes common security problems in web applications and how to avoid them with Rails. After reading it, you should be familiar with: * All countermeasures _(highlight)that are highlighted_ * The concept of sessions in Rails, what to put in there and popular attack methods @@ -385,7 +384,7 @@ params[:user] # => {:name => “ow3ned”, :admin => true} So if you create a new user using mass-assignment, it may be too easy to become an administrator. -Note that this vulnerability is not restricted to database columns. Any setter method, unless explicitly protected, is accessible via the <tt>attributes=</tt> method. In fact, this vulnerability is extended even further with the introduction of nested mass assignment (and nested object forms) in Rails 2.3<plus>. The +accepts_nested_attributes_for+ declaration provides us the ability to extend mass assignment to model associations (+has_many+, +has_one+, +has_and_belongs_to_many+). For example: +Note that this vulnerability is not restricted to database columns. Any setter method, unless explicitly protected, is accessible via the <tt>attributes=</tt> method. In fact, this vulnerability is extended even further with the introduction of nested mass assignment (and nested object forms) in Rails 2.3. The +accepts_nested_attributes_for+ declaration provides us the ability to extend mass assignment to model associations (+has_many+, +has_one+, +has_and_belongs_to_many+). For example: <ruby> class Person < ActiveRecord::Base @@ -628,7 +627,7 @@ h4. Whitelists versus Blacklists -- _When sanitizing, protecting or verifying something, whitelists over blacklists._ -A blacklist can be a list of bad e-mail addresses, non-public actions or bad HTML tags. This is opposed to a whitelist which lists the good e-mail addresses, public actions, good HTML tags and so on. Although, sometimes it is not possible to create a whitelist (in a SPAM filter, for example), _(highlight)prefer to use whitelist approaches_: +A blacklist can be a list of bad e-mail addresses, non-public actions or bad HTML tags. This is opposed to a whitelist which lists the good e-mail addresses, public actions, good HTML tags and so on. Although sometimes it is not possible to create a whitelist (in a SPAM filter, for example), _(highlight)prefer to use whitelist approaches_: * Use before_filter :only => [...] instead of :except => [...]. This way you don't forget to turn it off for newly added actions. * Use attr_accessible instead of attr_protected. See the mass-assignment section for details diff --git a/guides/source/testing.textile b/guides/source/testing.textile index 60b0aa89b9..d35be6a70e 100644 --- a/guides/source/testing.textile +++ b/guides/source/testing.textile @@ -412,7 +412,7 @@ NOTE: +assert_valid(record)+ has been deprecated. Please use +assert(record.vali |+assert_no_difference(expressions, message = nil, &block)+ |Asserts that the numeric result of evaluating an expression is not changed before and after invoking the passed in block.| |+assert_recognizes(expected_options, path, extras={}, message=nil)+ |Asserts that the routing of the given path was handled correctly and that the parsed options (given in the expected_options hash) match path. Basically, it asserts that Rails recognizes the route given by expected_options.| |+assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)+ |Asserts that the provided options can be used to generate the provided path. This is the inverse of assert_recognizes. The extras parameter is used to tell the request the names and values of additional request parameters that would be in a query string. The message parameter allows you to specify a custom error message for assertion failures.| -|+assert_response(type, message = nil)+ |Asserts that the response comes with a specific status code. You can specify +:success+ to indicate 200, +:redirect+ to indicate 300-399, +:missing+ to indicate 404, or +:error+ to match the 500-599 range| +|+assert_response(type, message = nil)+ |Asserts that the response comes with a specific status code. You can specify +:success+ to indicate 200-299, +:redirect+ to indicate 300-399, +:missing+ to indicate 404, or +:error+ to match the 500-599 range| |+assert_redirected_to(options = {}, message=nil)+ |Assert that the redirection options passed in match those of the redirect called in the latest action. This match can be partial, such that +assert_redirected_to(:controller => "weblog")+ will also match the redirection of +redirect_to(:controller => "weblog", :action => "show")+ and so on.| |+assert_template(expected = nil, message=nil)+ |Asserts that the request was rendered with the appropriate template file.| diff --git a/guides/source/upgrading_ruby_on_rails.textile b/guides/source/upgrading_ruby_on_rails.textile index e63548abc9..2b2e65c813 100644 --- a/guides/source/upgrading_ruby_on_rails.textile +++ b/guides/source/upgrading_ruby_on_rails.textile @@ -38,6 +38,10 @@ h4(#identity_map4_0). IdentityMap Rails 4.0 has removed <tt>IdentityMap</tt> from <tt>ActiveRecord</tt>, due to "some inconsistencies with associations":https://github.com/rails/rails/commit/302c912bf6bcd0fa200d964ec2dc4a44abe328a6. If you have manually enabled it in your application, you will have to remove the following config that has no effect anymore: <tt>config.active_record.identity_map</tt>. +h4(#active_model4_0). ActiveModel + +Rails 4.0 has changed how errors attach with the ConfirmationValidator. Now when confirmation validations fail the error will be attached to <tt>:#{attribute}_confirmation</tt> instead of <tt>attribute</tt>. + h3. Upgrading from Rails 3.1 to Rails 3.2 If your application is currently on any version of Rails older than 3.1.x, you should upgrade to Rails 3.1 before attempting an update to Rails 3.2. |