diff options
Diffstat (limited to 'guides')
-rw-r--r-- | guides/source/action_mailer_basics.textile | 4 | ||||
-rw-r--r-- | guides/source/active_record_querying.textile | 2 | ||||
-rw-r--r-- | guides/source/contributing_to_ruby_on_rails.textile | 2 | ||||
-rw-r--r-- | guides/source/engines.textile | 38 | ||||
-rw-r--r-- | guides/source/form_helpers.textile | 135 | ||||
-rw-r--r-- | guides/source/getting_started.textile | 1 | ||||
-rw-r--r-- | guides/source/testing.textile | 88 |
7 files changed, 198 insertions, 72 deletions
diff --git a/guides/source/action_mailer_basics.textile b/guides/source/action_mailer_basics.textile index 7c61cc4a8d..54e55d7260 100644 --- a/guides/source/action_mailer_basics.textile +++ b/guides/source/action_mailer_basics.textile @@ -508,8 +508,8 @@ class UserMailerTest < ActionMailer::TestCase # Test the body of the sent email contains what we expect it to assert_equal [user.email], email.to assert_equal "Welcome to My Awesome Site", email.subject - assert_match(/<h1>Welcome to example.com, #{user.name}<\/h1>/, email.encoded) - assert_match(/Welcome to example.com, #{user.name}/, email.encoded) + assert_match "<h1>Welcome to example.com, #{user.name}</h1>", email.body.to_s + assert_match "you have joined to example.com community", email.body.to_s end end </ruby> diff --git a/guides/source/active_record_querying.textile b/guides/source/active_record_querying.textile index 9863d16fda..f71a136b22 100644 --- a/guides/source/active_record_querying.textile +++ b/guides/source/active_record_querying.textile @@ -1043,7 +1043,7 @@ Even though Active Record lets you specify conditions on the eager loaded associ However if you must do this, you may use +where+ as you would normally. <ruby> -Post.includes(:comments).where("comments.visible", true) +Post.includes(:comments).where("comments.visible" => true) </ruby> This would generate a query which contains a +LEFT OUTER JOIN+ whereas the +joins+ method would generate one using the +INNER JOIN+ function instead. diff --git a/guides/source/contributing_to_ruby_on_rails.textile b/guides/source/contributing_to_ruby_on_rails.textile index fd5e2b28c0..a8a097d156 100644 --- a/guides/source/contributing_to_ruby_on_rails.textile +++ b/guides/source/contributing_to_ruby_on_rails.textile @@ -34,6 +34,8 @@ h4. What about Feature Requests? Please don't put "feature request" items into GitHub Issues. If there's a new feature that you want to see added to Ruby on Rails, you'll need to write the code yourself - or convince someone else to partner with you to write the code. Later in this guide you'll find detailed instructions for proposing a patch to Ruby on Rails. If you enter a wishlist item in GitHub Issues with no code, you can expect it to be marked "invalid" as soon as it's reviewed. +If you'd like feedback on an idea for a feature before doing the work for make a patch, please send an email to the "rails-core mailing list":https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core. You might get no response, which means that everyone is indifferent. You might find someone who's also interested in building that feature. You might get a "This won't be accepted." But it's the proper place to discuss new ideas. GitHub Issues are not a particularly good venue for the sometimes long and involved discussions new features require. + h3. Running the Test Suite To move on from submitting bugs to helping resolve existing issues or contributing your own code to Ruby on Rails, you _must_ be able to run its test suite. In this section of the guide you'll learn how to set up the tests on your own computer. diff --git a/guides/source/engines.textile b/guides/source/engines.textile index 86e7254201..53c2845731 100644 --- a/guides/source/engines.textile +++ b/guides/source/engines.textile @@ -347,7 +347,7 @@ The form will be making a +POST+ request to +/posts/:post_id/comments+, which wi <ruby> def create @post = Post.find(params[:post_id]) - @comment = @post.comments.build(params[:comment]) + @comment = @post.comments.create(params[:comment]) flash[:notice] = "Comment has been created!" redirect_to post_path end @@ -563,7 +563,7 @@ end By default, the engine's controllers inherit from <tt>Blorgh::ApplicationController</tt>. So, after making this change they will have access to the main applications +ApplicationController+ as though they were part of the main application. -This change does require that the engine is run from a Rails application that has an +ApplicationController+. +This change does require that the engine is run from a Rails application that has an +ApplicationController+. h4. Configuring an engine @@ -734,12 +734,14 @@ You can also specify these assets as dependencies of other assets using the Asse */ </plain> +INFO. Remember that in order to use languages like Sass or CoffeeScript, you should add the relevant library to your engine's +.gemspec+. + h4. Separate Assets & Precompiling There are some situations where your engine's assets not required by the host application. For example, say that you've created an admin functionality that only exists for your engine. In this case, the host application doesn't need to require +admin.css+ or +admin.js+. Only the gem's admin layout needs these assets. It doesn't make sense for the host app to include +"blorg/admin.css"+ in it's stylesheets. In this situation, you should explicitly define these assets for precompilation. -This tells sprockets to add you engine assets when +rake assets:precompile+ is ran. +This tells sprockets to add you engine assets when +rake assets:precompile+ is ran. You can define assets for precompilation in +engine.rb+ @@ -753,18 +755,40 @@ For more information, read the "Asset Pipeline guide":http://guides.rubyonrails. h4. Other gem dependencies -Gem dependencies inside an engine should be specified inside the +.gemspec+ file that's at the root of the engine. The reason for this is because the engine may be installed as a gem. If dependencies were to be specified inside the +Gemfile+, these would not be recognised by a traditional gem install and so they would not be installed, causing the engine to malfunction. +Gem dependencies inside an engine should be specified inside the +.gemspec+ file +that's at the root of the engine. The reason for this is because the engine may +be installed as a gem. If dependencies were to be specified inside the +Gemfile+, +these would not be recognised by a traditional gem install and so they would not +be installed, causing the engine to malfunction. -To specify a dependency that should be installed with the engine during a traditional +gem install+, specify it inside the +Gem::Specification+ block inside the +.gemspec+ file in the engine: +To specify a dependency that should be installed with the engine during a +traditional +gem install+, specify it inside the +Gem::Specification+ block +inside the +.gemspec+ file in the engine: <ruby> s.add_dependency "moo" </ruby> -To specify a dependency that should only be installed as a development dependency of the application, specify it like this: +To specify a dependency that should only be installed as a development +dependency of the application, specify it like this: <ruby> s.add_development_dependency "moo" </ruby> -Both kinds of dependencies will be installed when +bundle install+ is run inside the application. The development dependencies for the gem will only be used when the tests for the engine are running. +Both kinds of dependencies will be installed when +bundle install+ is run inside +the application. The development dependencies for the gem will only be used when +the tests for the engine are running. + +Note that if you want to immediately require dependencies when the engine is +required, you should require them before engine's initialization. For example: + +<ruby> +require 'other_engine/engine' +require 'yet_another_engine/engine' + +module MyEngine + class Engine < ::Rails::Engine + end +end +</ruby>
\ No newline at end of file diff --git a/guides/source/form_helpers.textile b/guides/source/form_helpers.textile index 1851aceff8..58338ce54b 100644 --- a/guides/source/form_helpers.textile +++ b/guides/source/form_helpers.textile @@ -10,7 +10,7 @@ In this guide you will: * Understand the date and time helpers Rails provides * Learn what makes a file upload form different * Learn some cases of building forms to external resources -* Find out where to look for complex forms +* Find out how to build complex forms endprologue. @@ -816,11 +816,130 @@ Or if you don't want to render an +authenticity_token+ field: h3. Building Complex Forms -Many apps grow beyond simple forms editing a single object. For example when creating a Person you might want to allow the user to (on the same form) create multiple address records (home, work, etc.). When later editing that person the user should be able to add, remove or amend addresses as necessary. While this guide has shown you all the pieces necessary to handle this, Rails does not yet have a standard end-to-end way of accomplishing this, but many have come up with viable approaches. These include: +Many apps grow beyond simple forms editing a single object. For example when creating a Person you might want to allow the user to (on the same form) create multiple address records (home, work, etc.). When later editing that person the user should be able to add, remove or amend addresses as necessary. -* As of Rails 2.3, Rails includes "Nested Attributes":./2_3_release_notes.html#nested-attributes and "Nested Object Forms":./2_3_release_notes.html#nested-object-forms -* Ryan Bates' series of Railscasts on "complex forms":http://railscasts.com/episodes/75 -* Handle Multiple Models in One Form from "Advanced Rails Recipes":http://media.pragprog.com/titles/fr_arr/multiple_models_one_form.pdf -* Eloy Duran's "complex-forms-examples":https://github.com/alloy/complex-form-examples/ application -* Lance Ivy's "nested_assignment":https://github.com/cainlevy/nested_assignment/tree/master plugin and "sample application":https://github.com/cainlevy/complex-form-examples/tree/cainlevy -* James Golick's "attribute_fu":https://github.com/jamesgolick/attribute_fu plugin +h4. Configuring the Model + +Active Record provides model level support via the +accepts_nested_attributes_for+ method: + +<ruby> +class Person < ActiveRecord::Base + has_many :addresses + accepts_nested_attributes_for :addresses + + attr_accessible :name, :addresses_attributes +end + +class Address < ActiveRecord::Base + belongs_to :person + attr_accessible :kind, :street +end +</ruby> + +This creates an +addresses_attributes=+ method on +Person+ that allows you to create, update and (optionally) destroy addresses. When using +attr_accessible+ or +attr_protected+ you must mark +addresses_attributes+ as accessible as well as the other attributes of +Person+ and +Address+ that should be mass assigned. + +h4. Building the Form + +The following form allows a user to create a +Person+ and its associated addresses. + +<erb> +<%= form_for @person do |f| %> + Addresses: + <ul> + <%= f.fields_for :addresses do |addresses_form| %> + <li> + <%= addresses_form.label :kind %> + <%= addresses_form.text_field :kind %> + + <%= addresses_form.label :street %> + <%= addresses_form.text_field :street %> + ... + </li> + <% end %> + </ul> +<% end %> +</erb> + + +When an association accepts nested attributes +fields_for+ renders its block once for every element of the association. In particular, if a person has no addresses it renders nothing. A common pattern is for the controller to build one or more empty children so that at least one set of fields is shown to the user. The example below would result in 3 sets of address fields being rendered on the new person form. + +<ruby> +def new + @person = Person.new + 3.times { @person.addresses.build} +end +</ruby> + ++fields_for+ yields a form builder that names parameters in the format expected the accessor generated by +accepts_nested_attributes_for+. For example when creating a user with 2 addresses, the submitted parameters would look like + +<ruby> +{ + :person => { + :name => 'John Doe', + :addresses_attributes => { + '0' => { + :kind => 'Home', + :street => '221b Baker Street', + }, + '1' => { + :kind => 'Office', + :street => '31 Spooner Street' + } + } + } +} +</ruby> + +The keys of the +:addresses_attributes+ hash are unimportant, they need merely be different for each address. + +If the associated object is already saved, +fields_for+ autogenerates a hidden input with the +id+ of the saved record. You can disable this by passing +:include_id => false+ to +fields_for+. You may wish to do this if the autogenerated input is placed in a location where an input tag is not valid HTML or when using an ORM where children do not have an id. + +h4. The Controller + +You do not need to write any specific controller code to use nested attributes. Create and update records as you would with a simple form. + +h4. Removing Objects + +You can allow users to delete associated objects by passing +allow_destroy => true+ to +accepts_nested_attributes_for+ + +<ruby> +class Person < ActiveRecord::Base + has_many :addresses + accepts_nested_attributes_for :addresses, :allow_destroy => true +end +</ruby> + +If the hash of attributes for an object contains the key +_destroy+ with a value of '1' or 'true' then the object will be destroyed. This form allows users to remove addresses: + +<erb> +<%= form_for @person do |f| %> + Addresses: + <ul> + <%= f.fields_for :addresses do |addresses_form| %> + <li> + <%= check_box :_destroy%> + <%= addresses_form.label :kind %> + <%= addresses_form.text_field :kind %> + ... + </li> + <% end %> + </ul> +<% end %> +</erb> + +h4. Preventing Empty Records + +It is often useful to ignore sets of fields that the user has not filled in. You can control this by passing a +:reject_if+ proc to +accepts_nested_attributes_for+. This proc will be called with each hash of attributes submitted by the form. If the proc returns +false+ then Active Record will not build an associated object for that hash. The example below only tries to build an address if the +kind+ attribute is set. + +<ruby> +class Person < ActiveRecord::Base + has_many :addresses + accepts_nested_attributes_for :addresses, :reject_if => lambda {|attributes| attributes['kind'].blank?} +end +</ruby> + +As a convenience you can instead pass the symbol +:all_blank+ which will create a proc that will reject records where all the attributes are blank excluding any value for +_destroy+. + +h4. Adding Fields on the Fly + +Rather than rendering multiple sets of fields ahead of time you may wish to add them only when a user clicks on an 'Add new child' button. Rails does not provide any builtin support for this. When generating new sets of fields you must ensure the the key of the associated array is unique - the current javascript date (milliseconds after the epoch) is a common choice.
\ No newline at end of file diff --git a/guides/source/getting_started.textile b/guides/source/getting_started.textile index 07419d11b4..b00d9af00c 100644 --- a/guides/source/getting_started.textile +++ b/guides/source/getting_started.textile @@ -1248,6 +1248,7 @@ First, take a look at +comment.rb+: <ruby> class Comment < ActiveRecord::Base belongs_to :post + attr_accessible :body, :commenter end </ruby> diff --git a/guides/source/testing.textile b/guides/source/testing.textile index d35be6a70e..4faf59fad8 100644 --- a/guides/source/testing.textile +++ b/guides/source/testing.textile @@ -1,62 +1,57 @@ h2. A Guide to Testing Rails Applications -This guide covers built-in mechanisms offered by Rails to test your application. By referring to this guide, you will be able to: +This guide covers built-in mechanisms offered by Rails to test your +application. By referring to this guide, you will be able to: * Understand Rails testing terminology -* Write unit, functional and integration tests for your application +* Write unit, functional, and integration tests for your application * Identify other popular testing approaches and plugins -This guide won't teach you to write a Rails application; it assumes basic familiarity with the Rails way of doing things. - endprologue. h3. Why Write Tests for your Rails Applications? -* Rails makes it super easy to write your tests. It starts by producing skeleton test code in the background while you are creating your models and controllers. -* By simply running your Rails tests you can ensure your code adheres to the desired functionality even after some major code refactoring. -* Rails tests can also simulate browser requests and thus you can test your application's response without having to test it through your browser. - -h3. Introduction to Testing - -Testing support was woven into the Rails fabric from the beginning. It wasn't an "oh! let's bolt on support for running tests because they're new and cool" epiphany. Just about every Rails application interacts heavily with a database - and, as a result, your tests will need a database to interact with as well. To write efficient tests, you'll need to understand how to set up this database and populate it with sample data. +Rails makes it super easy to write your tests. It starts by producing skeleton test code while you are creating your models and controllers. -h4. The Three Environments +By simply running your Rails tests you can ensure your code adheres to the desired functionality even after some major code refactoring. -Every Rails application you build has 3 sides: a side for production, a side for development, and a side for testing. +Rails tests can also simulate browser requests and thus you can test your application's response without having to test it through your browser. -One place you'll find this distinction is in the +config/database.yml+ file. This YAML configuration file has 3 different sections defining 3 unique database setups: +h3. Introduction to Testing -* production -* development -* test +Testing support was woven into the Rails fabric from the beginning. It wasn't an "oh! let's bolt on support for running tests because they're new and cool" epiphany. Just about every Rails application interacts heavily with a database and, as a result, your tests will need a database to interact with as well. To write efficient tests, you'll need to understand how to set up this database and populate it with sample data. -This allows you to set up and interact with test data without any danger of your tests altering data from your production environment. +h4. The Test Environment -For example, suppose you need to test your new +delete_this_user_and_every_everything_associated_with_it+ function. Wouldn't you want to run this in an environment where it makes no difference if you destroy data or not? +By default, every Rails application has three environments: development, test, and production. The database for each one of them is configured in +config/database.yml+. -When you do end up destroying your testing database (and it will happen, trust me), you can rebuild it from scratch according to the specs defined in the development database. You can do this by running +rake db:test:prepare+. +A dedicated test database allows you to set up and interact with test data in isolation. Tests can mangle test data with confidence, that won't touch the data in the development or production databases. h4. Rails Sets up for Testing from the Word Go Rails creates a +test+ folder for you as soon as you create a Rails project using +rails new+ _application_name_. If you list the contents of this folder then you shall see: <shell> -$ ls -F test/ +$ ls -F test -fixtures/ functional/ integration/ test_helper.rb unit/ +fixtures/ functional/ integration/ performance/ test_helper.rb unit/ </shell> -The +unit+ folder is meant to hold tests for your models, the +functional+ folder is meant to hold tests for your controllers, and the +integration+ folder is meant to hold tests that involve any number of controllers interacting. Fixtures are a way of organizing test data; they reside in the +fixtures+ folder. The +test_helper.rb+ file holds the default configuration for your tests. +The +unit+ directory is meant to hold tests for your models, the +functional+ directory is meant to hold tests for your controllers, the +integration+ directory is meant to hold tests that involve any number of controllers interacting, and the +performance+ directory is meant for performance tests. + +Fixtures are a way of organizing test data; they reside in the +fixtures+ folder. + +The +test_helper.rb+ file holds the default configuration for your tests. h4. The Low-Down on Fixtures For good tests, you'll need to give some thought to setting up test data. In Rails, you can handle this by defining and customizing fixtures. -h5. What are Fixtures? +h5. What Are Fixtures? -_Fixtures_ is a fancy word for sample data. Fixtures allow you to populate your testing database with predefined data before your tests run. Fixtures are database independent and assume a single format: *YAML*. +_Fixtures_ is a fancy word for sample data. Fixtures allow you to populate your testing database with predefined data before your tests run. Fixtures are database independent written in YAML. There is one file per model. -You'll find fixtures under your +test/fixtures+ directory. When you run +rails generate model+ to create a new model, fixture stubs will be automatically created and placed in this directory. +You'll find fixtures under your +test/fixtures+ directory. When you run +rails generate model+ to create a new model fixture stubs will be automatically created and placed in this directory. h5. YAML @@ -77,35 +72,20 @@ steve: profession: guy with keyboard </yaml> -Each fixture is given a name followed by an indented list of colon-separated key/value pairs. Records are separated by a blank space. You can place comments in a fixture file by using the # character in the first column. +Each fixture is given a name followed by an indented list of colon-separated key/value pairs. Records are typically separated by a blank space. You can place comments in a fixture file by using the # character in the first column. h5. ERB'in It Up -ERB allows you to embed ruby code within templates. YAML fixture format is pre-processed with ERB when you load fixtures. This allows you to use Ruby to help you generate some sample data. - -<erb> -<% earth_size = 20 %> -mercury: - size: <%= earth_size / 50 %> - brightest_on: <%= 113.days.ago.to_s(:db) %> - -venus: - size: <%= earth_size / 2 %> - brightest_on: <%= 67.days.ago.to_s(:db) %> - -mars: - size: <%= earth_size - 69 %> - brightest_on: <%= 13.days.from_now.to_s(:db) %> -</erb> - -Anything encased within the +ERB allows you to embed Ruby code within templates. The YAML fixture format is pre-processed with ERB when Rails loads fixtures. This allows you to use Ruby to help you generate some sample data. For example, the following code generates a thousand users: <erb> -<% %> +<% 1000.times do |n| %> +user_<%= n %>: + username: <%= "user%03d" % n %> + email: <%= "user%03d@example.com" % n %> +<% end %> </erb> -tag is considered Ruby code. When this fixture is loaded, the +size+ attribute of the three records will be set to 20/50, 20/2, and 20-69 respectively. The +brightest_on+ attribute will also be evaluated and formatted by Rails to be compatible with the database. - h5. Fixtures in Action Rails by default automatically loads all fixtures from the +test/fixtures+ folder for your unit and functional test. Loading involves three steps: @@ -377,12 +357,12 @@ There are a bunch of different types of assertions you can use. Here's the compl |_.Assertion |_.Purpose| |+assert( boolean, [msg] )+ |Ensures that the object/expression is true.| -|+assert_equal( obj1, obj2, [msg] )+ |Ensures that +obj1 == obj2+ is true.| -|+assert_not_equal( obj1, obj2, [msg] )+ |Ensures that +obj1 == obj2+ is false.| -|+assert_same( obj1, obj2, [msg] )+ |Ensures that +obj1.equal?(obj2)+ is true.| -|+assert_not_same( obj1, obj2, [msg] )+ |Ensures that +obj1.equal?(obj2)+ is false.| +|+assert_equal( expected, actual, [msg] )+ |Ensures that +expected == actual+ is true.| +|+assert_not_equal( expected, actual, [msg] )+ |Ensures that +expected != actual+ is true.| +|+assert_same( expected, actual, [msg] )+ |Ensures that +expected.equal?(actual)+ is true.| +|+assert_not_same( expected, actual, [msg] )+ |Ensures that +!expected.equal?(actual)+ is true.| |+assert_nil( obj, [msg] )+ |Ensures that +obj.nil?+ is true.| -|+assert_not_nil( obj, [msg] )+ |Ensures that +obj.nil?+ is false.| +|+assert_not_nil( obj, [msg] )+ |Ensures that +!obj.nil?+ is true.| |+assert_match( regexp, string, [msg] )+ |Ensures that a string matches the regular expression.| |+assert_no_match( regexp, string, [msg] )+ |Ensures that a string doesn't match the regular expression.| |+assert_in_delta( expecting, actual, delta, [msg] )+ |Ensures that the numbers +expecting+ and +actual+ are within +delta+ of each other.| @@ -526,7 +506,7 @@ You also have access to three instance variables in your functional tests: h4. Testing Templates and Layouts -If you want to make sure that the response rendered the correct template and layout, you can use the +assert_template+ +If you want to make sure that the response rendered the correct template and layout, you can use the +assert_template+ method: <ruby> |