1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Merge branch 'master' of github.com:lifo/docrails

This commit is contained in:
Vijay Dev 2012-07-15 19:36:19 +05:30
commit a80e1e437e
11 changed files with 218 additions and 38 deletions

View file

@ -127,16 +127,13 @@ module ActionDispatch
# with a new RouteSet instance. # with a new RouteSet instance.
# #
# The new instance is yielded to the passed block. Typically the block # The new instance is yielded to the passed block. Typically the block
# will create some routes using <tt>map.draw { map.connect ... }</tt>: # will create some routes using <tt>set.draw { match ... }</tt>:
# #
# with_routing do |set| # with_routing do |set|
# set.draw do |map| # set.draw do
# map.connect ':controller/:action/:id' # resources :users
# assert_equal(
# ['/content/10/show', {}],
# map.generate(:controller => 'content', :id => 10, :action => 'show')
# end
# end # end
# assert_equal "/users", users_path
# end # end
# #
def with_routing def with_routing

View file

@ -51,7 +51,18 @@ module ActiveRecord
# #
# allows you to access the +address+ attribute of the +User+ model without # allows you to access the +address+ attribute of the +User+ model without
# firing an additional query. This will often result in a # firing an additional query. This will often result in a
# performance improvement over a simple +join+ # performance improvement over a simple +join+.
#
# === conditions
#
# If you want to add conditions to your included models you'll have
# to explicitly reference them. For example:
#
# User.includes(:posts).where('posts.name = ?', 'example')
#
# Will throw an error, but this will work:
#
# User.includes(:posts).where('posts.name = ?', 'example').references(:posts)
def includes(*args) def includes(*args)
args.empty? ? self : spawn.includes!(*args) args.empty? ? self : spawn.includes!(*args)
end end
@ -63,6 +74,12 @@ module ActiveRecord
self self
end end
# Forces eager loading by performing a LEFT OUTER JOIN on +args+:
#
# User.eager_load(:posts)
# => SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, ...
# FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
# "users"."id"
def eager_load(*args) def eager_load(*args)
args.blank? ? self : spawn.eager_load!(*args) args.blank? ? self : spawn.eager_load!(*args)
end end
@ -72,6 +89,10 @@ module ActiveRecord
self self
end end
# Allows preloading of +args+, in the same way that +includes+ does:
#
# User.preload(:posts)
# => SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3)
def preload(*args) def preload(*args)
args.blank? ? self : spawn.preload!(*args) args.blank? ? self : spawn.preload!(*args)
end end
@ -147,7 +168,7 @@ module ActiveRecord
# User.group(:name) # User.group(:name)
# => SELECT "users".* FROM "users" GROUP BY name # => SELECT "users".* FROM "users" GROUP BY name
# #
# Returns an array with distinct records based on the `group` attribute: # Returns an array with distinct records based on the +group+ attribute:
# #
# User.select([:id, :name]) # User.select([:id, :name])
# => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo"> # => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">
@ -211,6 +232,10 @@ module ActiveRecord
self self
end end
# Performs a joins on +args+:
#
# User.joins(:posts)
# => SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
def joins(*args) def joins(*args)
args.compact.blank? ? self : spawn.joins!(*args) args.compact.blank? ? self : spawn.joins!(*args)
end end
@ -334,6 +359,10 @@ module ActiveRecord
self self
end end
# Allows to specify a HAVING clause. Note that you can't use HAVING
# without also specifying a GROUP clause.
#
# Order.having('SUM(price) > 30').group('user_id')
def having(opts, *rest) def having(opts, *rest)
opts.blank? ? self : spawn.having!(opts, *rest) opts.blank? ? self : spawn.having!(opts, *rest)
end end
@ -375,6 +404,8 @@ module ActiveRecord
self self
end end
# Specifies locking settings (default to +true+). For more information
# on locking, please see +ActiveRecord::Locking+.
def lock(locks = true) def lock(locks = true)
spawn.lock!(locks) spawn.lock!(locks)
end end
@ -423,6 +454,12 @@ module ActiveRecord
scoped.extending(NullRelation) scoped.extending(NullRelation)
end end
# Sets readonly attributes for the returned relation. If value is
# true (default), attempting to update a record will result in an error.
#
# users = User.readonly
# users.first.save
# => ActiveRecord::ReadOnlyRecord: ActiveRecord::ReadOnlyRecord
def readonly(value = true) def readonly(value = true)
spawn.readonly!(value) spawn.readonly!(value)
end end

View file

@ -7,7 +7,7 @@ module ActiveRecord
# #
# The default assumes a +sessions+ tables with columns: # The default assumes a +sessions+ tables with columns:
# +id+ (numeric primary key), # +id+ (numeric primary key),
# +session_id+ (text, or longtext if your session data exceeds 65K), and # +session_id+ (string, usually varchar; maximum length is 255), and
# +data+ (text or longtext; careful if your session data exceeds 65KB). # +data+ (text or longtext; careful if your session data exceeds 65KB).
# #
# The +session_id+ column should always be indexed for speedy lookups. # The +session_id+ column should always be indexed for speedy lookups.

View file

@ -26,11 +26,11 @@ class Time
# around_filter :set_time_zone # around_filter :set_time_zone
# #
# def set_time_zone # def set_time_zone
# old_time_zone = Time.zone # if logged_in?
# Time.zone = current_user.time_zone if logged_in? # Time.use_zone(current_user.time_zone) { yield }
# yield # else
# ensure # yield
# Time.zone = old_time_zone # end
# end # end
# end # end
def zone=(time_zone) def zone=(time_zone)

View file

@ -508,8 +508,8 @@ class UserMailerTest < ActionMailer::TestCase
# Test the body of the sent email contains what we expect it to # Test the body of the sent email contains what we expect it to
assert_equal [user.email], email.to assert_equal [user.email], email.to
assert_equal "Welcome to My Awesome Site", email.subject assert_equal "Welcome to My Awesome Site", email.subject
assert_match(/<h1>Welcome to example.com, #{user.name}<\/h1>/, email.encoded) assert_match "<h1>Welcome to example.com, #{user.name}</h1>", email.body.to_s
assert_match(/Welcome to example.com, #{user.name}/, email.encoded) assert_match "you have joined to example.com community", email.body.to_s
end end
end end
</ruby> </ruby>

View file

@ -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. However if you must do this, you may use +where+ as you would normally.
<ruby> <ruby>
Post.includes(:comments).where("comments.visible", true) Post.includes(:comments).where("comments.visible" => true)
</ruby> </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. This would generate a query which contains a +LEFT OUTER JOIN+ whereas the +joins+ method would generate one using the +INNER JOIN+ function instead.

View file

@ -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. 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 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. 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.

View file

@ -347,7 +347,7 @@ The form will be making a +POST+ request to +/posts/:post_id/comments+, which wi
<ruby> <ruby>
def create def create
@post = Post.find(params[:post_id]) @post = Post.find(params[:post_id])
@comment = @post.comments.build(params[:comment]) @comment = @post.comments.create(params[:comment])
flash[:notice] = "Comment has been created!" flash[:notice] = "Comment has been created!"
redirect_to post_path redirect_to post_path
end 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. 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 h4. Configuring an engine
@ -734,12 +734,14 @@ You can also specify these assets as dependencies of other assets using the Asse
*/ */
</plain> </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 h4. Separate Assets & Precompiling
There are some situations where your engine's assets not required by the host application. For example, say that you've created There are some situations where your engine's assets 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+ 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. 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+ 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 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> <ruby>
s.add_dependency "moo" s.add_dependency "moo"
</ruby> </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> <ruby>
s.add_development_dependency "moo" s.add_development_dependency "moo"
</ruby> </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>

View file

@ -10,7 +10,7 @@ In this guide you will:
* Understand the date and time helpers Rails provides * Understand the date and time helpers Rails provides
* Learn what makes a file upload form different * Learn what makes a file upload form different
* Learn some cases of building forms to external resources * 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. endprologue.
@ -816,11 +816,130 @@ Or if you don't want to render an +authenticity_token+ field:
h3. Building Complex Forms 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 h4. Configuring the Model
* 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 Active Record provides model level support via the +accepts_nested_attributes_for+ method:
* 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 <ruby>
* James Golick's "attribute_fu":https://github.com/jamesgolick/attribute_fu plugin 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.

View file

@ -1248,6 +1248,7 @@ First, take a look at +comment.rb+:
<ruby> <ruby>
class Comment < ActiveRecord::Base class Comment < ActiveRecord::Base
belongs_to :post belongs_to :post
attr_accessible :body, :commenter
end end
</ruby> </ruby>

View file

@ -357,12 +357,12 @@ There are a bunch of different types of assertions you can use. Here's the compl
|_.Assertion |_.Purpose| |_.Assertion |_.Purpose|
|+assert( boolean, [msg] )+ |Ensures that the object/expression is true.| |+assert( boolean, [msg] )+ |Ensures that the object/expression is true.|
|+assert_equal( obj1, obj2, [msg] )+ |Ensures that +obj1 == obj2+ is true.| |+assert_equal( expected, actual, [msg] )+ |Ensures that +expected == actual+ is true.|
|+assert_not_equal( obj1, obj2, [msg] )+ |Ensures that +obj1 == obj2+ is false.| |+assert_not_equal( expected, actual, [msg] )+ |Ensures that +expected != actual+ is true.|
|+assert_same( obj1, obj2, [msg] )+ |Ensures that +obj1.equal?(obj2)+ is true.| |+assert_same( expected, actual, [msg] )+ |Ensures that +expected.equal?(actual)+ is true.|
|+assert_not_same( obj1, obj2, [msg] )+ |Ensures that +obj1.equal?(obj2)+ is false.| |+assert_not_same( expected, actual, [msg] )+ |Ensures that +!expected.equal?(actual)+ is true.|
|+assert_nil( obj, [msg] )+ |Ensures that +obj.nil?+ 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_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_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.| |+assert_in_delta( expecting, actual, delta, [msg] )+ |Ensures that the numbers +expecting+ and +actual+ are within +delta+ of each other.|