aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/CHANGELOG.md4
-rw-r--r--actionpack/lib/abstract_controller/translation.rb8
-rw-r--r--actionpack/lib/action_controller/caching/sweeping.rb9
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb3
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb26
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb2
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb29
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb38
-rw-r--r--actionpack/lib/action_view/lookup_context.rb7
-rw-r--r--actionpack/lib/action_view/renderer/abstract_renderer.rb6
-rw-r--r--actionpack/lib/action_view/renderer/partial_renderer.rb2
-rw-r--r--actionpack/lib/action_view/renderer/template_renderer.rb5
-rw-r--r--actionpack/test/abstract/translation_test.rb13
-rw-r--r--actionpack/test/controller/filters_test.rb11
-rw-r--r--actionpack/test/dispatch/routing_test.rb52
-rw-r--r--actionpack/test/fixtures/test/_changing_priority.html.erb1
-rw-r--r--actionpack/test/fixtures/test/_changing_priority.json.erb1
-rw-r--r--actionpack/test/fixtures/test/_first_json_partial.json.erb1
-rw-r--r--actionpack/test/fixtures/test/_json_change_priority.json.erb0
-rw-r--r--actionpack/test/fixtures/test/_second_json_partial.json.erb1
-rw-r--r--actionpack/test/fixtures/test/change_priorty.html.erb2
-rw-r--r--actionpack/test/fixtures/test/html_template.html.erb1
-rw-r--r--actionpack/test/template/form_tag_helper_test.rb11
-rw-r--r--actionpack/test/template/render_test.rb10
-rw-r--r--actionpack/test/template/url_helper_test.rb14
-rw-r--r--activemodel/lib/active_model/validations/clusivity.rb9
-rw-r--r--activemodel/lib/active_model/validations/exclusion.rb3
-rw-r--r--activemodel/lib/active_model/validations/inclusion.rb3
-rw-r--r--activemodel/test/cases/validations/exclusion_validation_test.rb10
-rw-r--r--activemodel/test/cases/validations/i18n_validation_test.rb22
-rw-r--r--activemodel/test/cases/validations/inclusion_validation_test.rb10
-rw-r--r--activerecord/CHANGELOG.md17
-rw-r--r--activerecord/lib/active_record/associations.rb39
-rw-r--r--activerecord/lib/active_record/associations/association.rb8
-rw-r--r--activerecord/lib/active_record/associations/association_scope.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/association.rb8
-rw-r--r--activerecord/lib/active_record/associations/builder/collection_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb90
-rw-r--r--activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb39
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb8
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb11
-rw-r--r--activerecord/lib/active_record/core.rb2
-rw-r--r--activerecord/lib/active_record/migration/join_table.rb6
-rw-r--r--activerecord/lib/active_record/model.rb2
-rw-r--r--activerecord/lib/active_record/querying.rb6
-rw-r--r--activerecord/lib/active_record/reflection.rb4
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb2
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/migration_generator.rb31
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/templates/migration.rb10
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb69
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb113
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb7
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb2
-rw-r--r--activerecord/test/cases/base_test.rb6
-rw-r--r--activerecord/test/cases/explain_test.rb10
-rw-r--r--activerecord/test/cases/migration/change_schema_test.rb4
-rw-r--r--activerecord/test/cases/migration/column_attributes_test.rb8
-rw-r--r--activerecord/test/cases/migration/create_join_table_test.rb20
-rw-r--r--activerecord/test/cases/reflection_test.rb4
-rw-r--r--activerecord/test/cases/validations/uniqueness_validation_test.rb16
-rw-r--r--activerecord/test/models/author.rb74
-rw-r--r--activerecord/test/models/book.rb2
-rw-r--r--activerecord/test/models/car.rb6
-rw-r--r--activerecord/test/models/category.rb18
-rw-r--r--activerecord/test/models/company.rb67
-rw-r--r--activerecord/test/models/company_in_module.rb9
-rw-r--r--activerecord/test/models/developer.rb22
-rw-r--r--activerecord/test/models/liquid.rb2
-rw-r--r--activerecord/test/models/member.rb6
-rw-r--r--activerecord/test/models/person.rb12
-rw-r--r--activerecord/test/models/pirate.rb6
-rw-r--r--activerecord/test/models/post.rb35
-rw-r--r--activerecord/test/models/project.rb27
-rw-r--r--activerecord/test/models/sponsor.rb4
-rw-r--r--activerecord/test/models/tagging.rb5
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb102
-rw-r--r--activesupport/lib/active_support/tagged_logging.rb4
-rw-r--r--activesupport/test/multibyte_chars_test.rb2
-rw-r--r--guides/code/getting_started/app/views/comments/_comment.html.erb4
-rw-r--r--guides/code/getting_started/app/views/posts/index.html.erb2
-rw-r--r--guides/source/active_support_core_extensions.textile22
-rw-r--r--guides/source/ajax_on_rails.textile9
-rw-r--r--guides/source/association_basics.textile34
-rw-r--r--guides/source/getting_started.textile17
-rw-r--r--guides/source/layouts_and_rendering.textile2
-rw-r--r--railties/lib/rails/application.rb2
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/index.html.erb2
-rw-r--r--railties/lib/rails/generators/generated_attribute.rb13
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt2
-rw-r--r--railties/lib/rails/queueing.rb27
-rw-r--r--railties/test/application/queue_test.rb10
-rw-r--r--railties/test/generators/migration_generator_test.rb13
-rw-r--r--railties/test/generators_test.rb2
-rw-r--r--railties/test/queueing/container_test.rb30
97 files changed, 732 insertions, 728 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index be793d905f..da89a7a43c 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,9 @@
## Rails 4.0.0 (unreleased) ##
+* Remove `:confirm` in favor of `:data => { :confirm => "Text" }` option for `button_to`, `button_tag`, `image_submit_tag`, `link_to` and `submit_tag` helpers.
+
+ *Carlos Galdino*
+
* Show routes in exception page while debugging a `RoutingError` in development. *Richard Schneeman and Mattt Thompson*
* Add `ActionController::Flash.add_flash_types` method to allow people to register their own flash types. e.g.:
diff --git a/actionpack/lib/abstract_controller/translation.rb b/actionpack/lib/abstract_controller/translation.rb
index 6d68cf4944..b6c484d188 100644
--- a/actionpack/lib/abstract_controller/translation.rb
+++ b/actionpack/lib/abstract_controller/translation.rb
@@ -1,6 +1,12 @@
module AbstractController
module Translation
def translate(*args)
+ key = args.first
+ if key.is_a?(String) && (key[0] == '.')
+ key = "#{ controller_path.gsub('/', '.') }.#{ action_name }#{ key }"
+ args[0] = key
+ end
+
I18n.translate(*args)
end
alias :t :translate
@@ -10,4 +16,4 @@ module AbstractController
end
alias :l :localize
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/caching/sweeping.rb b/actionpack/lib/action_controller/caching/sweeping.rb
index 39da15e26a..73291ce083 100644
--- a/actionpack/lib/action_controller/caching/sweeping.rb
+++ b/actionpack/lib/action_controller/caching/sweeping.rb
@@ -68,14 +68,14 @@ module ActionController #:nodoc:
def after(controller)
self.controller = controller
callback(:after) if controller.perform_caching
- # Clean up, so that the controller can be collected after this request
- self.controller = nil
end
def around(controller)
before(controller)
yield
after(controller)
+ ensure
+ clean_up
end
protected
@@ -90,6 +90,11 @@ module ActionController #:nodoc:
end
private
+ def clean_up
+ # Clean up, so that the controller can be collected after this request
+ self.controller = nil
+ end
+
def callback(timing)
controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}"
action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}"
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 0b800c3c62..4665fea91a 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -182,7 +182,8 @@ module ActionController #:nodoc:
# end
# end
#
- # Be sure to check respond_with and respond_to documentation for more examples.
+ # Be sure to check the documentation of +respond_with+ and
+ # <tt>ActionController::MimeResponds.respond_to</tt> for more examples.
def respond_to(*mimes, &block)
raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index a49b12621e..0a65b4dbcc 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -262,7 +262,7 @@ module ActionDispatch
# for root cases, where the latter is the correct one.
def self.normalize_path(path)
path = Journey::Router::Utils.normalize_path(path)
- path.gsub!(%r{/(\(+)/?}, '\1/') unless path =~ %r{^/\(+[^/]+\)$}
+ path.gsub!(%r{/(\(+)/?}, '\1/') unless path =~ %r{^/\(+[^)]+\)$}
path
end
@@ -917,7 +917,7 @@ module ActionDispatch
@path = (options[:path] || @name).to_s
@controller = (options[:controller] || @name).to_s
@as = options[:as]
- @param = options[:param] || :id
+ @param = (options[:param] || :id).to_sym
@options = options
end
@@ -965,12 +965,18 @@ module ActionDispatch
"#{path}/:#{param}"
end
+ alias :shallow_scope :member_scope
+
def new_scope(new_path)
"#{path}/#{new_path}"
end
+ def nested_param
+ :"#{singular}_#{param}"
+ end
+
def nested_scope
- "#{path}/:#{singular}_#{param}"
+ "#{path}/:#{nested_param}"
end
end
@@ -1481,18 +1487,18 @@ module ActionDispatch
def nested_options #:nodoc:
options = { :as => parent_resource.member_name }
options[:constraints] = {
- :"#{parent_resource.singular}_id" => id_constraint
- } if id_constraint?
+ parent_resource.nested_param => param_constraint
+ } if param_constraint?
options
end
- def id_constraint? #:nodoc:
- @scope[:constraints] && @scope[:constraints][:id].is_a?(Regexp)
+ def param_constraint? #:nodoc:
+ @scope[:constraints] && @scope[:constraints][parent_resource.param].is_a?(Regexp)
end
- def id_constraint #:nodoc:
- @scope[:constraints][:id]
+ def param_constraint #:nodoc:
+ @scope[:constraints][parent_resource.param]
end
def canonical_action?(action, flag) #:nodoc:
@@ -1505,7 +1511,7 @@ module ActionDispatch
def path_for_action(action, path) #:nodoc:
prefix = shallow_scoping? ?
- "#{@scope[:shallow_path]}/#{parent_resource.path}/:id" : @scope[:path]
+ "#{@scope[:shallow_path]}/#{parent_resource.shallow_scope}" : @scope[:path]
if canonical_action?(action, path.blank?)
prefix.to_s
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 1d6ca0c78d..0bbed6cbea 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -333,7 +333,7 @@ module ActionDispatch
end
end
- MountedHelpers.class_eval <<-RUBY
+ MountedHelpers.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
def #{name}
@#{name} ||= _#{name}
end
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 1a0019a48c..4b8484bfb5 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -382,9 +382,7 @@ module ActionView
# Creates a submit button with the text <tt>value</tt> as the caption.
#
# ==== Options
- # * <tt>:confirm => 'question?'</tt> - If present the unobtrusive JavaScript
- # drivers will provide a prompt with the question specified. If the user accepts,
- # the form is processed normally, otherwise no action is taken.
+ # * <tt>:data</tt> - This option can be used to add custom data attributes.
# * <tt>:disabled</tt> - If true, the user will not be able to use this input.
# * Any other key creates standard HTML options for the tag.
#
@@ -407,16 +405,12 @@ module ActionView
# submit_tag "Edit", :class => "edit_button"
# # => <input class="edit_button" name="commit" type="submit" value="Edit" />
#
- # submit_tag "Save", :confirm => "Are you sure?"
+ # submit_tag "Save", :data => { :confirm => "Are you sure?" }
# # => <input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />
#
def submit_tag(value = "Save changes", options = {})
options = options.stringify_keys
- if confirm = options.delete("confirm")
- options["data-confirm"] = confirm
- end
-
tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options)
end
@@ -428,10 +422,7 @@ module ActionView
# so this helper will also accept a block.
#
# ==== Options
- # * <tt>:confirm => 'question?'</tt> - If present, the
- # unobtrusive JavaScript drivers will provide a prompt with
- # the question specified. If the user accepts, the form is
- # processed normally, otherwise no action is taken.
+ # * <tt>:data</tt> - This option can be used to add custom data attributes.
# * <tt>:disabled</tt> - If true, the user will not be able to
# use this input.
# * Any other key creates standard HTML options for the tag.
@@ -452,10 +443,6 @@ module ActionView
options ||= {}
options = options.stringify_keys
- if confirm = options.delete("confirm")
- options["data-confirm"] = confirm
- end
-
options.reverse_merge! 'name' => 'button', 'type' => 'submit'
content_tag :button, content_or_options || 'Button', options, &block
@@ -466,9 +453,7 @@ module ActionView
# <tt>source</tt> is passed to AssetTagHelper#path_to_image
#
# ==== Options
- # * <tt>:confirm => 'question?'</tt> - This will add a JavaScript confirm
- # prompt with the question specified. If the user accepts, the form is
- # processed normally, otherwise no action is taken.
+ # * <tt>:data</tt> - This option can be used to add custom data attributes.
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
# * Any other key creates standard HTML options for the tag.
#
@@ -485,15 +470,11 @@ module ActionView
# image_submit_tag("agree.png", :disabled => true, :class => "agree_disagree_button")
# # => <input class="agree_disagree_button" disabled="disabled" src="/images/agree.png" type="image" />
#
- # image_submit_tag("save.png", :confirm => "Are you sure?")
+ # image_submit_tag("save.png", :data => { :confirm => "Are you sure?" })
# # => <input src="/images/save.png" data-confirm="Are you sure?" type="image" />
def image_submit_tag(source, options = {})
options = options.stringify_keys
- if confirm = options.delete("confirm")
- options["data-confirm"] = confirm
- end
-
tag :input, { "type" => "image", "src" => path_to_image(source) }.update(options)
end
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 736f9fa2f0..b4eb3d4826 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -132,8 +132,7 @@ module ActionView
# # posts_path
#
# link_to(body, url_options = {}, html_options = {})
- # # url_options, except :confirm or :method,
- # # is passed to url_for
+ # # url_options, except :method, is passed to url_for
#
# link_to(options = {}, html_options = {}) do
# # name
@@ -144,9 +143,7 @@ module ActionView
# end
#
# ==== Options
- # * <tt>:confirm => 'question?'</tt> - This will allow the unobtrusive JavaScript
- # driver to prompt with the question specified. If the user accepts, the link is
- # processed normally, otherwise no action is taken.
+ # * <tt>:data</tt> - This option can be used to add custom data attributes.
# * <tt>:method => symbol of HTTP verb</tt> - This modifier will dynamically
# create an HTML form and immediately submit the form for processing using
# the HTTP verb specified. Useful for having links perform a POST operation
@@ -226,13 +223,15 @@ module ActionView
# link_to "Nonsense search", searches_path(:foo => "bar", :baz => "quux")
# # => <a href="/searches?foo=bar&amp;baz=quux">Nonsense search</a>
#
- # The two options specific to +link_to+ (<tt>:confirm</tt> and <tt>:method</tt>) are used as follows:
+ # The only option specific to +link_to+ (<tt>:method</tt>) is used as follows:
#
- # link_to "Visit Other Site", "http://www.rubyonrails.org/", :confirm => "Are you sure?"
- # # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?"">Visit Other Site</a>
+ # link_to("Destroy", "http://www.example.com", :method => :delete)
+ # # => <a href='http://www.example.com' rel="nofollow" data-method="delete">Destroy</a>
+ #
+ # You can also use custom data attributes using the <tt>:data</tt> option:
#
- # link_to("Destroy", "http://www.example.com", :method => :delete, :confirm => "Are you sure?")
- # # => <a href='http://www.example.com' rel="nofollow" data-method="delete" data-confirm="Are you sure?">Destroy</a>
+ # link_to "Visit Other Site", "http://www.rubyonrails.org/", :data => { :confirm => "Are you sure?" }
+ # # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?"">Visit Other Site</a>
def link_to(name = nil, options = nil, html_options = nil, &block)
html_options, options = options, name if block_given?
options ||= {}
@@ -255,10 +254,9 @@ module ActionView
# to allow styling of the form itself and its children. This can be changed
# using the <tt>:form_class</tt> modifier within +html_options+. You can control
# the form submission and input element behavior using +html_options+.
- # This method accepts the <tt>:method</tt> and <tt>:confirm</tt> modifiers
- # described in the +link_to+ documentation. If no <tt>:method</tt> modifier
- # is given, it will default to performing a POST operation. You can also
- # disable the button by passing <tt>:disabled => true</tt> in +html_options+.
+ # This method accepts the <tt>:method</tt> modifier described in the +link_to+ documentation.
+ # If no <tt>:method</tt> modifier is given, it will default to performing a POST operation.
+ # You can also disable the button by passing <tt>:disabled => true</tt> in +html_options+.
# If you are using RESTful routes, you can pass the <tt>:method</tt>
# to change the HTTP verb used to submit the form.
#
@@ -269,9 +267,7 @@ module ActionView
# * <tt>:method</tt> - Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>,
# <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>. By default it will be <tt>:post</tt>.
# * <tt>:disabled</tt> - If set to true, it will generate a disabled button.
- # * <tt>:confirm</tt> - This will use the unobtrusive JavaScript driver to
- # prompt with the question specified. If the user accepts, the link is
- # processed normally, otherwise no action is taken.
+ # * <tt>:data</tt> - This option can be used to add custom data attributes.
# * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
# submit behavior. By default this behavior is an ajax submit.
# * <tt>:form</tt> - This hash will be form attributes
@@ -311,7 +307,7 @@ module ActionView
#
#
# <%= button_to "Delete Image", { :action => "delete", :id => @image.id },
- # :confirm => "Are you sure?", :method => :delete %>
+ # :method => :delete, :data => { :confirm => "Are you sure?" } %>
# # => "<form method="post" action="/images/delete/1" class="button_to">
# # <div>
# # <input type="hidden" name="_method" value="delete" />
@@ -321,8 +317,8 @@ module ActionView
# # </form>"
#
#
- # <%= button_to('Destroy', 'http://www.example.com', :confirm => 'Are you sure?',
- # :method => "delete", :remote => true) %>
+ # <%= button_to('Destroy', 'http://www.example.com',
+ # :method => "delete", :remote => true, :data => { :confirm' => 'Are you sure?' }) %>
# # => "<form class='button_to' method='post' action='http://www.example.com' data-remote='true'>
# # <div>
# # <input name='_method' value='delete' type='hidden' />
@@ -627,10 +623,8 @@ module ActionView
html_options = html_options.stringify_keys
html_options['data-remote'] = 'true' if link_to_remote_options?(options) || link_to_remote_options?(html_options)
- confirm = html_options.delete('confirm')
method = html_options.delete('method')
- html_options["data-confirm"] = confirm if confirm
add_method_to_attributes!(html_options, method) if method
html_options
diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb
index 00989ec405..47dd932c71 100644
--- a/actionpack/lib/action_view/lookup_context.rb
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -96,7 +96,7 @@ module ActionView
# Helpers related to template lookup using the lookup context information.
module ViewPaths
- attr_reader :view_paths
+ attr_reader :view_paths, :html_fallback_for_js
# Whenever setting view paths, makes a copy so we can manipulate then in
# instance objects as we wish.
@@ -184,7 +184,10 @@ module ActionView
def formats=(values)
if values
values.concat(default_formats) if values.delete "*/*"
- values << :html if values == [:js]
+ if values == [:js]
+ values << :html
+ @html_fallback_for_js = true
+ end
end
super(values)
end
diff --git a/actionpack/lib/action_view/renderer/abstract_renderer.rb b/actionpack/lib/action_view/renderer/abstract_renderer.rb
index e3d8e9d508..6fb8cbb46c 100644
--- a/actionpack/lib/action_view/renderer/abstract_renderer.rb
+++ b/actionpack/lib/action_view/renderer/abstract_renderer.rb
@@ -22,5 +22,11 @@ module ActionView
def instrument(name, options={})
ActiveSupport::Notifications.instrument("render_#{name}.action_view", options){ yield }
end
+
+ def prepend_formats(formats)
+ formats = Array(formats)
+ return if formats.empty? || @lookup_context.html_fallback_for_js
+ @lookup_context.formats = formats | @lookup_context.formats
+ end
end
end
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
index 9100545718..a08a566b35 100644
--- a/actionpack/lib/action_view/renderer/partial_renderer.rb
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -320,6 +320,8 @@ module ActionView
@block = block
@details = extract_details(options)
+ prepend_formats(options[:formats])
+
if String === partial
@object = options[:object]
@path = partial
diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb
index 3c1b11396a..156ad4e547 100644
--- a/actionpack/lib/action_view/renderer/template_renderer.rb
+++ b/actionpack/lib/action_view/renderer/template_renderer.rb
@@ -8,9 +8,10 @@ module ActionView
template = determine_template(options)
context = @lookup_context
+ prepend_formats(template.formats)
+
unless context.rendered_format
- context.formats = template.formats unless template.formats.empty?
- context.rendered_format = context.formats.first
+ context.rendered_format = template.formats.first || formats.last
end
render_template(template, options[:layout], options[:locals])
diff --git a/actionpack/test/abstract/translation_test.rb b/actionpack/test/abstract/translation_test.rb
index 0194ee943f..99064a8b87 100644
--- a/actionpack/test/abstract/translation_test.rb
+++ b/actionpack/test/abstract/translation_test.rb
@@ -23,4 +23,17 @@ class TranslationControllerTest < ActiveSupport::TestCase
def test_action_controller_base_responds_to_l
assert_respond_to @controller, :l
end
+
+ def test_lazy_lookup
+ expected = 'bar'
+ @controller.stubs(:action_name => :index)
+ I18n.stubs(:translate).with('action_controller.base.index.foo').returns(expected)
+ assert_equal expected, @controller.t('.foo')
+ end
+
+ def test_default_translation
+ key, expected = 'one.two' 'bar'
+ I18n.stubs(:translate).with(key).returns(expected)
+ assert_equal expected, @controller.t(key)
+ end
end
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index b9cb93f0f4..afc00a3c9d 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -505,6 +505,10 @@ class FilterTest < ActionController::TestCase
def show
render :text => 'hello world'
end
+
+ def error
+ raise StandardError.new
+ end
end
class ImplicitActionsController < ActionController::Base
@@ -534,6 +538,13 @@ class FilterTest < ActionController::TestCase
assert_equal 'hello world', response.body
end
+ def test_sweeper_should_clean_up_if_exception_is_raised
+ assert_raise StandardError do
+ test_process(SweeperTestController, 'error')
+ end
+ assert_nil AppSweeper.instance.controller
+ end
+
def test_before_method_of_sweeper_should_always_return_true
sweeper = ActionController::Caching::Sweeper.send(:new)
assert sweeper.before(TestController.new)
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index fed26d89f8..205238990e 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -482,11 +482,17 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
get :preview, :on => :member
end
- resources :profiles, :param => :username do
+ resources :profiles, :param => :username, :username => /[a-z]+/ do
get :details, :on => :member
resources :messages
end
+ resources :orders do
+ constraints :download => /[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}/ do
+ resources :downloads, :param => :download, :shallow => true
+ end
+ end
+
scope :as => "routes" do
get "/c/:id", :as => :collision, :to => "collision#show"
get "/collision", :to => "collision#show"
@@ -2243,6 +2249,23 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal '34', @request.params[:id]
end
+ def test_custom_param_constraint
+ get '/profiles/bob1'
+ assert_equal 404, @response.status
+
+ get '/profiles/bob1/details'
+ assert_equal 404, @response.status
+
+ get '/profiles/bob1/messages/34'
+ assert_equal 404, @response.status
+ end
+
+ def test_shallow_custom_param
+ get '/downloads/0c0c0b68-d24b-11e1-a861-001ff3fffe6f.zip'
+ assert_equal 'downloads#show', @response.body
+ assert_equal '0c0c0b68-d24b-11e1-a861-001ff3fffe6f', @request.params[:download]
+ end
+
private
def with_https
old_https = https?
@@ -2679,3 +2702,30 @@ class TestInvalidUrls < ActionDispatch::IntegrationTest
end
end
end
+
+class TestOptionalRootSegments < ActionDispatch::IntegrationTest
+ stub_controllers do |routes|
+ Routes = routes
+ Routes.draw do
+ get '/(page/:page)', :to => 'pages#index', :as => :root
+ end
+ end
+
+ def app
+ Routes
+ end
+
+ include Routes.url_helpers
+
+ def test_optional_root_segments
+ get '/'
+ assert_equal 'pages#index', @response.body
+ assert_equal '/', root_path
+
+ get '/page/1'
+ assert_equal 'pages#index', @response.body
+ assert_equal '1', @request.params[:page]
+ assert_equal '/page/1', root_path('1')
+ assert_equal '/page/1', root_path(:page => '1')
+ end
+end
diff --git a/actionpack/test/fixtures/test/_changing_priority.html.erb b/actionpack/test/fixtures/test/_changing_priority.html.erb
new file mode 100644
index 0000000000..3225efc49a
--- /dev/null
+++ b/actionpack/test/fixtures/test/_changing_priority.html.erb
@@ -0,0 +1 @@
+HTML \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/_changing_priority.json.erb b/actionpack/test/fixtures/test/_changing_priority.json.erb
new file mode 100644
index 0000000000..7fa41dce66
--- /dev/null
+++ b/actionpack/test/fixtures/test/_changing_priority.json.erb
@@ -0,0 +1 @@
+JSON \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/_first_json_partial.json.erb b/actionpack/test/fixtures/test/_first_json_partial.json.erb
new file mode 100644
index 0000000000..790ee896db
--- /dev/null
+++ b/actionpack/test/fixtures/test/_first_json_partial.json.erb
@@ -0,0 +1 @@
+<%= render :partial => "test/second_json_partial" %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/_json_change_priority.json.erb b/actionpack/test/fixtures/test/_json_change_priority.json.erb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/actionpack/test/fixtures/test/_json_change_priority.json.erb
diff --git a/actionpack/test/fixtures/test/_second_json_partial.json.erb b/actionpack/test/fixtures/test/_second_json_partial.json.erb
new file mode 100644
index 0000000000..5ebb7f1afd
--- /dev/null
+++ b/actionpack/test/fixtures/test/_second_json_partial.json.erb
@@ -0,0 +1 @@
+Third level \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/change_priorty.html.erb b/actionpack/test/fixtures/test/change_priorty.html.erb
new file mode 100644
index 0000000000..5618977d05
--- /dev/null
+++ b/actionpack/test/fixtures/test/change_priorty.html.erb
@@ -0,0 +1,2 @@
+<%= render :partial => "test/json_change_priority", formats: :json %>
+HTML Template, but <%= render :partial => "test/changing_priority" %> partial \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/html_template.html.erb b/actionpack/test/fixtures/test/html_template.html.erb
new file mode 100644
index 0000000000..1bbc2b7f09
--- /dev/null
+++ b/actionpack/test/fixtures/test/html_template.html.erb
@@ -0,0 +1 @@
+<%= render :partial => "test/first_json_partial", formats: :json %> \ No newline at end of file
diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb
index 5d19e3274d..05baaa5a85 100644
--- a/actionpack/test/template/form_tag_helper_test.rb
+++ b/actionpack/test/template/form_tag_helper_test.rb
@@ -382,7 +382,7 @@ class FormTagHelperTest < ActionView::TestCase
def test_submit_tag_with_confirmation
assert_dom_equal(
%(<input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />),
- submit_tag("Save", :confirm => "Are you sure?")
+ submit_tag("Save", :data => { :confirm => "Are you sure?" })
)
end
@@ -437,10 +437,17 @@ class FormTagHelperTest < ActionView::TestCase
assert_dom_equal('<button name="temptation" type="button"><strong>Do not press me</strong></button>', output)
end
+ def test_button_tag_with_confirmation
+ assert_dom_equal(
+ %(<button name="button" type="submit" data-confirm="Are you sure?">Save</button>),
+ button_tag("Save", :type => "submit", :data => { :confirm => "Are you sure?" })
+ )
+ end
+
def test_image_submit_tag_with_confirmation
assert_dom_equal(
%(<input type="image" src="/images/save.gif" data-confirm="Are you sure?" />),
- image_submit_tag("save.gif", :confirm => "Are you sure?")
+ image_submit_tag("save.gif", :data => { :confirm => "Are you sure?" })
)
end
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index 88ed8664c2..3ce1d20bd9 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -54,6 +54,16 @@ module RenderTestCases
assert_equal "Hello world", @view.render(:template => "test/one", :formats => [:html])
end
+ def test_render_partial_implicitly_use_format_of_the_rendered_partial
+ @view.lookup_context.formats = [:html]
+ assert_equal "Third level", @view.render(:template => "test/html_template")
+ end
+
+ def test_render_partial_use_last_prepended_format_for_partials_with_the_same_names
+ @view.lookup_context.formats = [:html]
+ assert_equal "\nHTML Template, but JSON partial", @view.render(:template => "test/change_priorty")
+ end
+
def test_render_template_with_a_missing_partial_of_another_format
@view.lookup_context.formats = [:html]
assert_raise ActionView::Template::Error, "Missing partial /missing with {:locale=>[:en], :formats=>[:json], :handlers=>[:erb, :builder]}" do
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index 62608a727f..a09409635d 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -90,7 +90,7 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_button_to_with_javascript_confirm
assert_dom_equal(
"<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :confirm => "Are you sure?")
+ button_to("Hello", "http://www.example.com", :data => { :confirm => "Are you sure?" })
)
end
@@ -108,7 +108,7 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_button_to_with_remote_and_javascript_confirm
assert_dom_equal(
"<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :remote => true, :confirm => "Are you sure?")
+ button_to("Hello", "http://www.example.com", :remote => true, :data => { :confirm => "Are you sure?" })
)
end
@@ -208,15 +208,15 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_link_tag_with_javascript_confirm
assert_dom_equal(
"<a href=\"http://www.example.com\" data-confirm=\"Are you sure?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :confirm => "Are you sure?")
+ link_to("Hello", "http://www.example.com", :data => { :confirm => "Are you sure?" })
)
assert_dom_equal(
"<a href=\"http://www.example.com\" data-confirm=\"You can't possibly be sure, can you?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :confirm => "You can't possibly be sure, can you?")
+ link_to("Hello", "http://www.example.com", :data => { :confirm => "You can't possibly be sure, can you?" })
)
assert_dom_equal(
"<a href=\"http://www.example.com\" data-confirm=\"You can't possibly be sure,\n can you?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :confirm => "You can't possibly be sure,\n can you?")
+ link_to("Hello", "http://www.example.com", :data => { :confirm => "You can't possibly be sure,\n can you?" })
)
end
@@ -265,14 +265,14 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_link_tag_using_post_javascript_and_confirm
assert_dom_equal(
"<a href=\"http://www.example.com\" data-method=\"post\" rel=\"nofollow\" data-confirm=\"Are you serious?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :method => :post, :confirm => "Are you serious?")
+ link_to("Hello", "http://www.example.com", :method => :post, :data => { :confirm => "Are you serious?" })
)
end
def test_link_tag_using_delete_javascript_and_href_and_confirm
assert_dom_equal(
"<a href='\#' rel=\"nofollow\" data-confirm=\"Are you serious?\" data-method=\"delete\">Destroy</a>",
- link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#', :confirm => "Are you serious?"),
+ link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#', :data => { :confirm => "Are you serious?" }),
"When specifying url, form should be generated with it, but not this.href"
)
end
diff --git a/activemodel/lib/active_model/validations/clusivity.rb b/activemodel/lib/active_model/validations/clusivity.rb
index 676457ec0f..643a6f2b7c 100644
--- a/activemodel/lib/active_model/validations/clusivity.rb
+++ b/activemodel/lib/active_model/validations/clusivity.rb
@@ -4,10 +4,10 @@ module ActiveModel
module Validations
module Clusivity #:nodoc:
ERROR_MESSAGE = "An object with the method #include? or a proc or lambda is required, " <<
- "and must be supplied as the :in option of the configuration hash"
+ "and must be supplied as the :in (or :within) option of the configuration hash"
def check_validity!
- unless [:include?, :call].any?{ |method| options[:in].respond_to?(method) }
+ unless [:include?, :call].any?{ |method| delimiter.respond_to?(method) }
raise ArgumentError, ERROR_MESSAGE
end
end
@@ -15,11 +15,14 @@ module ActiveModel
private
def include?(record, value)
- delimiter = options[:in]
exclusions = delimiter.respond_to?(:call) ? delimiter.call(record) : delimiter
exclusions.send(inclusion_method(exclusions), value)
end
+ def delimiter
+ @delimiter ||= options[:in] || options[:within]
+ end
+
# In Ruby 1.9 <tt>Range#include?</tt> on non-numeric ranges checks all possible values in the
# range for equality, so it may be slow for large ranges. The new <tt>Range#cover?</tt>
# uses the previous logic of comparing a value with the range endpoints.
diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb
index 3331162bc0..dc3368c569 100644
--- a/activemodel/lib/active_model/validations/exclusion.rb
+++ b/activemodel/lib/active_model/validations/exclusion.rb
@@ -9,7 +9,7 @@ module ActiveModel
def validate_each(record, attribute, value)
if include?(record, value)
- record.errors.add(attribute, :exclusion, options.except(:in).merge!(:value => value))
+ record.errors.add(attribute, :exclusion, options.except(:in, :within).merge!(:value => value))
end
end
end
@@ -30,6 +30,7 @@ module ActiveModel
# * <tt>:in</tt> - An enumerable object of items that the value shouldn't
# be part of. This can be supplied as a proc or lambda which returns an
# enumerable. If the enumerable is a range the test is performed with
+ # * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
# <tt>Range#cover?</tt>, otherwise with <tt>include?</tt>.
# * <tt>:message</tt> - Specifies a custom error message (default is: "is
# reserved").
diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb
index c995e28d64..c2835c550b 100644
--- a/activemodel/lib/active_model/validations/inclusion.rb
+++ b/activemodel/lib/active_model/validations/inclusion.rb
@@ -9,7 +9,7 @@ module ActiveModel
def validate_each(record, attribute, value)
unless include?(record, value)
- record.errors.add(attribute, :inclusion, options.except(:in).merge!(:value => value))
+ record.errors.add(attribute, :inclusion, options.except(:in, :within).merge!(:value => value))
end
end
end
@@ -30,6 +30,7 @@ module ActiveModel
# supplied as a proc or lambda which returns an enumerable. If the
# enumerable is a range the test is performed with <tt>Range#cover?</tt>,
# otherwise with <tt>include?</tt>.
+ # * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
# * <tt>:message</tt> - Specifies a custom error message (default is: "is
# not included in the list").
# * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the
diff --git a/activemodel/test/cases/validations/exclusion_validation_test.rb b/activemodel/test/cases/validations/exclusion_validation_test.rb
index adab8ccb2b..baccf72ecb 100644
--- a/activemodel/test/cases/validations/exclusion_validation_test.rb
+++ b/activemodel/test/cases/validations/exclusion_validation_test.rb
@@ -28,6 +28,16 @@ class ExclusionValidationTest < ActiveModel::TestCase
assert_equal ["option monkey is restricted"], t.errors[:title]
end
+ def test_validates_exclusion_of_with_within_option
+ Topic.validates_exclusion_of( :title, :within => %w( abe monkey ) )
+
+ assert Topic.new("title" => "something", "content" => "abc")
+
+ t = Topic.new("title" => "monkey")
+ assert t.invalid?
+ assert t.errors[:title].any?
+ end
+
def test_validates_exclusion_of_for_ruby_class
Person.validates_exclusion_of :karma, :in => %w( abe monkey )
diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb
index bb751cf9c5..4f8b7327c0 100644
--- a/activemodel/test/cases/validations/i18n_validation_test.rb
+++ b/activemodel/test/cases/validations/i18n_validation_test.rb
@@ -159,6 +159,17 @@ class I18nValidationTest < ActiveModel::TestCase
end
end
+ # validates_inclusion_of using :within w/ mocha
+
+ COMMON_CASES.each do |name, validation_options, generate_message_options|
+ test "validates_inclusion_of using :within on generated message #{name}" do
+ Person.validates_inclusion_of :title, validation_options.merge(:within => %w(a b c))
+ @person.title = 'z'
+ @person.errors.expects(:generate_message).with(:title, :inclusion, generate_message_options.merge(:value => 'z'))
+ @person.valid?
+ end
+ end
+
# validates_exclusion_of w/ mocha
COMMON_CASES.each do |name, validation_options, generate_message_options|
@@ -170,6 +181,17 @@ class I18nValidationTest < ActiveModel::TestCase
end
end
+ # validates_exclusion_of using :within w/ mocha
+
+ COMMON_CASES.each do |name, validation_options, generate_message_options|
+ test "validates_exclusion_of using :within generated message #{name}" do
+ Person.validates_exclusion_of :title, validation_options.merge(:within => %w(a b c))
+ @person.title = 'a'
+ @person.errors.expects(:generate_message).with(:title, :exclusion, generate_message_options.merge(:value => 'a'))
+ @person.valid?
+ end
+ end
+
# validates_numericality_of without :only_integer w/ mocha
COMMON_CASES.each do |name, validation_options, generate_message_options|
diff --git a/activemodel/test/cases/validations/inclusion_validation_test.rb b/activemodel/test/cases/validations/inclusion_validation_test.rb
index 851d345eab..c57fa75faf 100644
--- a/activemodel/test/cases/validations/inclusion_validation_test.rb
+++ b/activemodel/test/cases/validations/inclusion_validation_test.rb
@@ -60,6 +60,16 @@ class InclusionValidationTest < ActiveModel::TestCase
assert_equal ["option uhoh is not in the list"], t.errors[:title]
end
+ def test_validates_inclusion_of_with_within_option
+ Topic.validates_inclusion_of( :title, :within => %w( a b c d e f g ) )
+
+ assert Topic.new("title" => "a", "content" => "abc").valid?
+
+ t = Topic.new("title" => "uhoh", "content" => "abc")
+ assert t.invalid?
+ assert t.errors[:title].any?
+ end
+
def test_validates_inclusion_of_for_ruby_class
Person.validates_inclusion_of :karma, :in => %w( abe monkey )
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 36d94fb38b..08aa3aae34 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,22 @@
## Rails 4.0.0 (unreleased) ##
+* Removed `:finder_sql` and `:counter_sql` collection association options. Please
+ use scopes instead.
+
+ *Jon Leighton*
+
+* Removed `:insert_sql` and `:delete_sql` `has_and_belongs_to_many`
+ association options. Please use `has_many :through` instead.
+
+ *Jon Leighton*
+
+* The migration generator now creates a join table with (commented) indexes every time
+ the migration name contains the word `join_table`:
+
+ rails g migration create_join_table_for_artists_and_musics artist_id:index music_id
+
+ *Aleksey Magusev*
+
* Add `add_reference` and `remove_reference` schema statements. Aliases, `add_belongs_to`
and `remove_belongs_to` are acceptable. References are reversible.
Examples:
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index a90c5a2a9a..6124fe315f 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1104,15 +1104,6 @@ module ActiveRecord
# a +belongs_to+, and the records which get deleted are the join records, rather than
# the associated records.
#
- # [:finder_sql]
- # Specify a complete SQL statement to fetch the association. This is a good way to go for complex
- # associations that depend on multiple tables. May be supplied as a string or a proc where interpolation is
- # required. Note: When this option is used, +find_in_collection+
- # is _not_ added.
- # [:counter_sql]
- # Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
- # specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by
- # replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
# [:extend]
# Specify a named module for extending the proxy. See "Association extensions".
# [:include]
@@ -1184,15 +1175,7 @@ module ActiveRecord
# has_many :tags, :as => :taggable
# has_many :reports, :readonly => true
# has_many :subscribers, :through => :subscriptions, :source => :user
- # has_many :subscribers, :class_name => "Person", :finder_sql => Proc.new {
- # %Q{
- # SELECT DISTINCT *
- # FROM people p, post_subscriptions ps
- # WHERE ps.post_id = #{id} AND ps.person_id = p.id
- # ORDER BY p.first_name
- # }
- # }
- def has_many(name, scope = {}, options = nil, &extension)
+ def has_many(name, scope = nil, options = {}, &extension)
Builder::HasMany.build(self, name, scope, options, &extension)
end
@@ -1308,7 +1291,7 @@ module ActiveRecord
# has_one :boss, :readonly => :true
# has_one :club, :through => :membership
# has_one :primary_address, :through => :addressables, :conditions => ["addressable.primary = ?", true], :source => :addressable
- def has_one(name, scope = {}, options = nil)
+ def has_one(name, scope = nil, options = {})
Builder::HasOne.build(self, name, scope, options)
end
@@ -1431,7 +1414,7 @@ module ActiveRecord
# belongs_to :post, :counter_cache => true
# belongs_to :company, :touch => true
# belongs_to :company, :touch => :employees_last_updated_at
- def belongs_to(name, scope = {}, options = nil)
+ def belongs_to(name, scope = nil, options = {})
Builder::BelongsTo.build(self, name, scope, options)
end
@@ -1555,18 +1538,6 @@ module ActiveRecord
# such as <tt>last_name, first_name DESC</tt>
# [:uniq]
# If true, duplicate associated objects will be ignored by accessors and query methods.
- # [:finder_sql]
- # Overwrite the default generated SQL statement used to fetch the association with a manual statement
- # [:counter_sql]
- # Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
- # specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by
- # replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
- # [:delete_sql]
- # Overwrite the default generated SQL statement used to remove links between the associated
- # classes with a manual statement.
- # [:insert_sql]
- # Overwrite the default generated SQL statement used to add links between the associated classes
- # with a manual statement.
# [:extend]
# Anonymous module for extending the proxy, see "Association extensions".
# [:include]
@@ -1603,9 +1574,7 @@ module ActiveRecord
# has_and_belongs_to_many :nations, :class_name => "Country"
# has_and_belongs_to_many :categories, :join_table => "prods_cats"
# has_and_belongs_to_many :categories, :readonly => true
- # has_and_belongs_to_many :active_projects, :join_table => 'developers_projects', :delete_sql =>
- # proc { |record| "DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}" }
- def has_and_belongs_to_many(name, scope = {}, options = nil, &extension)
+ def has_and_belongs_to_many(name, scope = nil, options = {}, &extension)
Builder::HasAndBelongsToMany.build(self, name, scope, options, &extension)
end
end
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index 4b989793da..7db6d658f6 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -140,14 +140,6 @@ module ActiveRecord
reset
end
- def interpolate(sql, record = nil)
- if sql.respond_to?(:to_proc)
- owner.send(:instance_exec, record, &sql)
- else
- sql
- end
- end
-
# We can't dump @reflection since it contains the scope proc
def marshal_dump
reflection = @reflection
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb
index 1303822868..cb97490ef1 100644
--- a/activerecord/lib/active_record/associations/association_scope.rb
+++ b/activerecord/lib/active_record/associations/association_scope.rb
@@ -5,7 +5,7 @@ module ActiveRecord
attr_reader :association, :alias_tracker
- delegate :klass, :owner, :reflection, :interpolate, :to => :association
+ delegate :klass, :owner, :reflection, :to => :association
delegate :chain, :scope_chain, :options, :source_options, :active_record, :to => :reflection
def initialize(association)
diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb
index 4a78a9c076..f45ab1aff4 100644
--- a/activerecord/lib/active_record/associations/builder/association.rb
+++ b/activerecord/lib/active_record/associations/builder/association.rb
@@ -16,12 +16,12 @@ module ActiveRecord::Associations::Builder
@model = model
@name = name
- if options
- @scope = scope
- @options = options
- else
+ if scope.is_a?(Hash)
@scope = nil
@options = scope
+ else
+ @scope = scope
+ @options = options
end
if @scope && @scope.arity == 0
diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb
index af81af4ad2..b28d6a746c 100644
--- a/activerecord/lib/active_record/associations/builder/collection_association.rb
+++ b/activerecord/lib/active_record/associations/builder/collection_association.rb
@@ -3,7 +3,7 @@ module ActiveRecord::Associations::Builder
CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
def valid_options
- super + [:table_name, :finder_sql, :counter_sql, :before_add, :after_add, :before_remove, :after_remove]
+ super + [:table_name, :before_add, :after_add, :before_remove, :after_remove]
end
attr_reader :block_extension, :extension_module
diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
index 7f79ef25f2..a30e2dab26 100644
--- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
@@ -5,7 +5,7 @@ module ActiveRecord::Associations::Builder
end
def valid_options
- super + [:join_table, :association_foreign_key, :delete_sql, :insert_sql]
+ super + [:join_table, :association_foreign_key]
end
def build
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 55628c81bb..30522e3a5d 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -44,7 +44,7 @@ module ActiveRecord
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
def ids_reader
- if loaded? || options[:finder_sql]
+ if loaded?
load_target.map do |record|
record.send(reflection.association_primary_key)
end
@@ -79,11 +79,7 @@ module ActiveRecord
if block_given?
load_target.find(*args) { |*block_args| yield(*block_args) }
else
- if options[:finder_sql]
- find_by_scan(*args)
- else
- scoped.find(*args)
- end
+ scoped.find(*args)
end
end
@@ -170,35 +166,26 @@ module ActiveRecord
end
end
- # Count all records using SQL. If the +:counter_sql+ or +:finder_sql+ option is set for the
- # association, it will be used for the query. Otherwise, construct options and pass them with
+ # Count all records using SQL. Construct options and pass them with
# scope to the target class's +count+.
def count(column_name = nil, count_options = {})
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
- if options[:counter_sql] || options[:finder_sql]
- unless count_options.blank?
- raise ArgumentError, "If finder_sql/counter_sql is used then options cannot be passed"
- end
-
- reflection.klass.count_by_sql(custom_counter_sql)
- else
- if association_scope.uniq_value
- # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
- column_name ||= reflection.klass.primary_key
- count_options[:distinct] = true
- end
+ if association_scope.uniq_value
+ # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
+ column_name ||= reflection.klass.primary_key
+ count_options[:distinct] = true
+ end
- value = scoped.count(column_name, count_options)
+ value = scoped.count(column_name, count_options)
- limit = options[:limit]
- offset = options[:offset]
+ limit = options[:limit]
+ offset = options[:offset]
- if limit || offset
- [ [value - offset.to_i, 0].max, limit.to_i ].min
- else
- value
- end
+ if limit || offset
+ [ [value - offset.to_i, 0].max, limit.to_i ].min
+ else
+ value
end
end
@@ -298,9 +285,9 @@ module ActiveRecord
end
end
- def uniq(collection = load_target)
+ def uniq
seen = {}
- collection.find_all do |record|
+ load_target.find_all do |record|
seen[record.id] = true unless seen.key?(record.id)
end
end
@@ -323,7 +310,6 @@ module ActiveRecord
if record.new_record?
include_in_memory?(record)
else
- load_target if options[:finder_sql]
loaded? ? target.include?(record) : scoped.exists?(record)
end
else
@@ -358,32 +344,8 @@ module ActiveRecord
private
- def custom_counter_sql
- if options[:counter_sql]
- interpolate(options[:counter_sql])
- else
- # replace the SELECT clause with COUNT(SELECTS), preserving any hints within /* ... */
- interpolate(options[:finder_sql]).sub(/SELECT\b(\/\*.*?\*\/ )?(.*)\bFROM\b/im) do
- count_with = $2.to_s
- count_with = '*' if count_with.blank? || count_with =~ /,/
- "SELECT #{$1}COUNT(#{count_with}) FROM"
- end
- end
- end
-
- def custom_finder_sql
- interpolate(options[:finder_sql])
- end
-
def find_target
- records =
- if options[:finder_sql]
- reflection.klass.find_by_sql(custom_finder_sql)
- else
- scoped.all
- end
-
- records = options[:uniq] ? uniq(records) : records
+ records = scoped.to_a
records.each { |record| set_inverse_instance(record) }
records
end
@@ -522,7 +484,6 @@ module ActiveRecord
# Otherwise, go to the database only if none of the following are true:
# * target already loaded
# * owner is new record
- # * custom :finder_sql exists
# * target contains new or changed record(s)
# * the first arg is an integer (which indicates the number of records to be returned)
def fetch_first_or_last_using_find?(args)
@@ -531,7 +492,6 @@ module ActiveRecord
else
!(loaded? ||
owner.new_record? ||
- options[:finder_sql] ||
target.any? { |record| record.new_record? || record.changed? } ||
args.first.kind_of?(Integer))
end
@@ -548,20 +508,6 @@ module ActiveRecord
end
end
- # If using a custom finder_sql, #find scans the entire collection.
- def find_by_scan(*args)
- expects_array = args.first.kind_of?(Array)
- ids = args.flatten.compact.map{ |arg| arg.to_i }.uniq
-
- if ids.size == 1
- id = ids.first
- record = load_target.detect { |r| id == r.id }
- expects_array ? [ record ] : record
- else
- load_target.select { |r| ids.include?(r.id) }
- end
- end
-
# Fetches the first/last using SQL if possible, otherwise from the target array.
def first_or_last(type, *args)
args.shift if args.first.is_a?(Hash) && args.first.empty?
diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
index 93618721bb..e5b40f3911 100644
--- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
@@ -18,16 +18,12 @@ module ActiveRecord
end
end
- if options[:insert_sql]
- owner.connection.insert(interpolate(options[:insert_sql], record))
- else
- stmt = join_table.compile_insert(
- join_table[reflection.foreign_key] => owner.id,
- join_table[reflection.association_foreign_key] => record.id
- )
+ stmt = join_table.compile_insert(
+ join_table[reflection.foreign_key] => owner.id,
+ join_table[reflection.association_foreign_key] => record.id
+ )
- owner.connection.insert stmt
- end
+ owner.connection.insert stmt
record
end
@@ -39,22 +35,17 @@ module ActiveRecord
end
def delete_records(records, method)
- if sql = options[:delete_sql]
- records = load_target if records == :all
- records.each { |record| owner.connection.delete(interpolate(sql, record)) }
- else
- relation = join_table
- condition = relation[reflection.foreign_key].eq(owner.id)
-
- unless records == :all
- condition = condition.and(
- relation[reflection.association_foreign_key]
- .in(records.map { |x| x.id }.compact)
- )
- end
-
- owner.connection.delete(relation.where(condition).compile_delete)
+ relation = join_table
+ condition = relation[reflection.foreign_key].eq(owner.id)
+
+ unless records == :all
+ condition = condition.and(
+ relation[reflection.association_foreign_key]
+ .in(records.map { |x| x.id }.compact)
+ )
end
+
+ owner.connection.delete(relation.where(condition).compile_delete)
end
def invertible_for?(record)
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index 669b7e03c2..41c6ca92cc 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -33,13 +33,7 @@ module ActiveRecord
# If the collection is empty the target is set to an empty array and
# the loaded flag is set to true as well.
def count_records
- count = if has_cached_counter?
- owner.send(:read_attribute, cached_counter_attribute_name)
- elsif options[:counter_sql] || options[:finder_sql]
- reflection.klass.count_by_sql(custom_counter_sql)
- else
- scoped.count
- end
+ count = has_cached_counter? ? owner[cached_counter_attribute_name] : scoped.count
# If there's nothing in the database and @target has no new records
# we are certain the current target is an empty array. This is a
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index e7fd72994f..fa77a8733b 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -93,7 +93,7 @@ module ActiveRecord
end
def reflection_scope
- @reflection_scope ||= reflection.scope ? klass.unscoped.instance_exec(&reflection.scope) : klass.unscoped
+ @reflection_scope ||= reflection.scope ? klass.unscoped.instance_exec(nil, &reflection.scope) : klass.unscoped
end
def build_scope
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index ef17dfbbc5..f9ae1ed475 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -271,7 +271,7 @@ module ActiveRecord
# Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
# <tt>:updated_at</tt> to the table.
def timestamps(*args)
- options = { :null => false }.merge(args.extract_options!)
+ options = args.extract_options!
column(:created_at, :datetime, options)
column(:updated_at, :datetime, options)
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 3c3f01223c..a80c1cec39 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -204,11 +204,14 @@ module ActiveRecord
join_table_name = find_join_table_name(table_1, table_2, options)
column_options = options.delete(:column_options) || {}
- column_options.reverse_merge!({:null => false})
+ column_options.reverse_merge!(null: false)
- create_table(join_table_name, options.merge!(:id => false)) do |td|
- td.integer :"#{table_1.to_s.singularize}_id", column_options
- td.integer :"#{table_2.to_s.singularize}_id", column_options
+ t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
+
+ create_table(join_table_name, options.merge!(id: false)) do |td|
+ td.integer t1_column, column_options
+ td.integer t2_column, column_options
+ yield td if block_given?
end
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 90f156456e..803d68187c 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -380,7 +380,7 @@ module ActiveRecord
#
# So we can avoid the method_missing hit by explicitly defining #to_ary as nil here.
#
- # See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary/
+ # See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary.html
def to_ary # :nodoc:
nil
end
diff --git a/activerecord/lib/active_record/migration/join_table.rb b/activerecord/lib/active_record/migration/join_table.rb
index 01a580781b..e880ae97bb 100644
--- a/activerecord/lib/active_record/migration/join_table.rb
+++ b/activerecord/lib/active_record/migration/join_table.rb
@@ -4,13 +4,11 @@ module ActiveRecord
private
def find_join_table_name(table_1, table_2, options = {})
- options.delete(:table_name) { join_table_name(table_1, table_2) }
+ options.delete(:table_name) || join_table_name(table_1, table_2)
end
def join_table_name(table_1, table_2)
- tables_names = [table_1, table_2].map(&:to_s).sort
-
- tables_names.join("_").to_sym
+ [table_1, table_2].sort.join("_").to_sym
end
end
end
diff --git a/activerecord/lib/active_record/model.rb b/activerecord/lib/active_record/model.rb
index 0015e3a567..fb3fb643ff 100644
--- a/activerecord/lib/active_record/model.rb
+++ b/activerecord/lib/active_record/model.rb
@@ -74,9 +74,9 @@ module ActiveRecord
include Inheritance
include Scoping
include Sanitization
- include Integration
include AttributeAssignment
include ActiveModel::Conversion
+ include Integration
include Validations
include CounterCache
include Locking::Optimistic
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index 4d8283bcff..609d810654 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -62,8 +62,10 @@ module ActiveRecord
#
# Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
def count_by_sql(sql)
- sql = sanitize_conditions(sql)
- connection.select_value(sql, "#{name} Count").to_i
+ logging_query_plan do
+ sql = sanitize_conditions(sql)
+ connection.select_value(sql, "#{name} Count").to_i
+ end
end
end
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 3e8d9718f5..dede453e38 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -116,10 +116,6 @@ module ActiveRecord
active_record == other_aggregation.active_record
end
- def sanitized_conditions #:nodoc:
- @sanitized_conditions ||= klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
- end
-
private
def derive_class_name
name.to_s.camelize
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index 9e4b588ac2..5a24135f8e 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -26,7 +26,7 @@ module ActiveRecord
relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.send(:id))) if record.persisted?
Array(options[:scope]).each do |scope_item|
- scope_value = record.send(scope_item)
+ scope_value = record.read_attribute(scope_item)
reflection = record.class.reflect_on_association(scope_item)
if reflection
scope_value = record.send(reflection.foreign_key)
diff --git a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
index 1509e34473..f6a432c6e5 100644
--- a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
@@ -11,15 +11,36 @@ module ActiveRecord
end
protected
- attr_reader :migration_action
+ attr_reader :migration_action, :join_tables
- def set_local_assigns!
- if file_name =~ /^(add|remove)_.*_(?:to|from)_(.*)/
- @migration_action = $1
- @table_name = $2.pluralize
+ def set_local_assigns!
+ case file_name
+ when /^(add|remove)_.*_(?:to|from)_(.*)/
+ @migration_action = $1
+ @table_name = $2.pluralize
+ when /join_table/
+ if attributes.length == 2
+ @migration_action = 'join'
+ @join_tables = attributes.map(&:plural_name)
+
+ set_index_names
end
end
+ end
+
+ def set_index_names
+ attributes.each_with_index do |attr, i|
+ attr.index_name = [attr, attributes[i - 1]].map{ |a| index_name_for(a) }
+ end
+ end
+ def index_name_for(attribute)
+ if attribute.foreign_key?
+ attribute.name
+ else
+ attribute.name.singularize.foreign_key
+ end.to_sym
+ end
end
end
end
diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
index 34eaffcb0f..d5c07aecd3 100644
--- a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
+++ b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
@@ -12,6 +12,14 @@ class <%= migration_class_name %> < ActiveRecord::Migration
<%- end -%>
<%- end -%>
end
+<%- elsif migration_action == 'join' -%>
+ def change
+ create_join_table :<%= join_tables.first %>, :<%= join_tables.second %> do |t|
+ <%- attributes.each do |attribute| -%>
+ <%= '# ' unless attribute.has_index? -%>t.index <%= attribute.index_name %><%= attribute.inject_index_options %>
+ <%- end -%>
+ end
+ end
<%- else -%>
def up
<% attributes.each do |attribute| -%>
@@ -40,4 +48,4 @@ class <%= migration_class_name %> < ActiveRecord::Migration
<%- end -%>
end
<%- end -%>
-end \ No newline at end of file
+end
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index 24bb4adf0a..90e5467f13 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -65,16 +65,6 @@ class DeveloperWithSymbolsForKeys < ActiveRecord::Base
:foreign_key => "developer_id"
end
-class DeveloperWithCounterSQL < ActiveRecord::Base
- self.table_name = 'developers'
- has_and_belongs_to_many :projects,
- :class_name => "DeveloperWithCounterSQL",
- :join_table => "developers_projects",
- :association_foreign_key => "project_id",
- :foreign_key => "developer_id",
- :counter_sql => proc { "SELECT COUNT(*) AS count_all FROM projects INNER JOIN developers_projects ON projects.id = developers_projects.project_id WHERE developers_projects.developer_id =#{id}" }
-end
-
class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
:parrots, :pirates, :parrots_pirates, :treasures, :price_estimates, :tags, :taggings
@@ -361,31 +351,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal 0, david.projects(true).size
end
- def test_deleting_with_sql
- david = Developer.find(1)
- active_record = Project.find(1)
- active_record.developers.reload
- assert_equal 3, active_record.developers_by_sql.size
-
- active_record.developers_by_sql.delete(david)
- assert_equal 2, active_record.developers_by_sql(true).size
- end
-
- def test_deleting_array_with_sql
- active_record = Project.find(1)
- active_record.developers.reload
- assert_equal 3, active_record.developers_by_sql.size
-
- active_record.developers_by_sql.delete(Developer.all)
- assert_equal 0, active_record.developers_by_sql(true).size
- end
-
- def test_deleting_all_with_sql
- project = Project.find(1)
- project.developers_by_sql.delete_all
- assert_equal 0, project.developers_by_sql.size
- end
-
def test_deleting_all
david = Developer.find(1)
david.projects.reload
@@ -534,25 +499,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert ! project.developers.include?(developer)
end
- def test_find_in_association_with_custom_finder_sql
- assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id), "SQL find"
-
- active_record = projects(:active_record)
- active_record.developers_with_finder_sql.reload
- assert_equal developers(:david), active_record.developers_with_finder_sql.find(developers(:david).id), "Ruby find"
- end
-
- def test_find_in_association_with_custom_finder_sql_and_multiple_interpolations
- # interpolate once:
- assert_equal [developers(:david), developers(:jamis), developers(:poor_jamis)], projects(:active_record).developers_with_finder_sql, "first interpolation"
- # interpolate again, for a different project id
- assert_equal [developers(:david)], projects(:action_controller).developers_with_finder_sql, "second interpolation"
- end
-
- def test_find_in_association_with_custom_finder_sql_and_string_id
- assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id.to_s), "SQL find"
- end
-
def test_find_with_merged_options
assert_equal 1, projects(:active_record).limited_developers.size
assert_equal 1, projects(:active_record).limited_developers.all.size
@@ -788,21 +734,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal 2, david.projects.count
end
- def test_count_with_counter_sql
- developer = DeveloperWithCounterSQL.create(:name => 'tekin')
- developer.project_ids = [projects(:active_record).id]
- developer.save
- developer.reload
- assert_equal 1, developer.projects.count
- end
-
- unless current_adapter?(:PostgreSQLAdapter)
- def test_count_with_finder_sql
- assert_equal 3, projects(:active_record).developers_with_finder_sql.count
- assert_equal 3, projects(:active_record).developers_with_multiline_finder_sql.count
- end
- end
-
def test_association_proxy_transaction_method_starts_transaction_in_association_class
Post.expects(:transaction)
Category.first.posts.transaction do
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 477bb91412..4b7a2db096 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -20,43 +20,6 @@ require 'models/car'
require 'models/bulb'
require 'models/engine'
-class HasManyAssociationsTestForCountWithFinderSql < ActiveRecord::TestCase
- class Invoice < ActiveRecord::Base
- has_many :custom_line_items, :class_name => 'LineItem', :finder_sql => "SELECT line_items.* from line_items"
- end
- def test_should_fail
- assert_raise(ArgumentError) do
- Invoice.create.custom_line_items.count(:conditions => {:amount => 0})
- end
- end
-end
-
-class HasManyAssociationsTestForCountWithCountSql < ActiveRecord::TestCase
- class Invoice < ActiveRecord::Base
- has_many :custom_line_items, :class_name => 'LineItem', :counter_sql => "SELECT COUNT(*) line_items.* from line_items"
- end
- def test_should_fail
- assert_raise(ArgumentError) do
- Invoice.create.custom_line_items.count(:conditions => {:amount => 0})
- end
- end
-end
-
-class HasManyAssociationsTestForCountDistinctWithFinderSql < ActiveRecord::TestCase
- class Invoice < ActiveRecord::Base
- has_many :custom_line_items, :class_name => 'LineItem', :finder_sql => "SELECT DISTINCT line_items.amount from line_items"
- end
-
- def test_should_count_distinct_results
- invoice = Invoice.new
- invoice.custom_line_items << LineItem.new(:amount => 0)
- invoice.custom_line_items << LineItem.new(:amount => 0)
- invoice.save!
-
- assert_equal 1, invoice.custom_line_items.count
- end
-end
-
class HasManyAssociationsTestForReorderWithJoinDependency < ActiveRecord::TestCase
fixtures :authors, :posts, :comments
@@ -307,37 +270,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal "Summit", Firm.scoped(:order => "id").first.clients_using_primary_key.first.name
end
- def test_finding_using_sql
- firm = Firm.scoped(:order => "id").first
- first_client = firm.clients_using_sql.first
- assert_not_nil first_client
- assert_equal "Microsoft", first_client.name
- assert_equal 1, firm.clients_using_sql.size
- assert_equal 1, Firm.scoped(:order => "id").first.clients_using_sql.size
- end
-
- def test_finding_using_sql_take_into_account_only_uniq_ids
- firm = Firm.scoped(:order => "id").first
- client = firm.clients_using_sql.first
- assert_equal client, firm.clients_using_sql.find(client.id, client.id)
- assert_equal client, firm.clients_using_sql.find(client.id, client.id.to_s)
- end
-
- def test_counting_using_sql
- assert_equal 1, Firm.scoped(:order => "id").first.clients_using_counter_sql.size
- assert Firm.scoped(:order => "id").first.clients_using_counter_sql.any?
- assert_equal 0, Firm.scoped(:order => "id").first.clients_using_zero_counter_sql.size
- assert !Firm.scoped(:order => "id").first.clients_using_zero_counter_sql.any?
- end
-
- def test_counting_non_existant_items_using_sql
- assert_equal 0, Firm.scoped(:order => "id").first.no_clients_using_counter_sql.size
- end
-
- def test_counting_using_finder_sql
- assert_equal 2, Firm.find(4).clients_using_sql.count
- end
-
def test_belongs_to_sanity
c = Client.new
assert_nil c.firm
@@ -365,22 +297,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::RecordNotFound) { firm.clients.find(2, 99) }
end
- def test_find_string_ids_when_using_finder_sql
- firm = Firm.scoped(:order => "id").first
-
- client = firm.clients_using_finder_sql.find("2")
- assert_kind_of Client, client
-
- client_ary = firm.clients_using_finder_sql.find(["2"])
- assert_kind_of Array, client_ary
- assert_equal client, client_ary.first
-
- client_ary = firm.clients_using_finder_sql.find("2", "3")
- assert_kind_of Array, client_ary
- assert_equal 2, client_ary.size
- assert client_ary.include?(client)
- end
-
def test_find_all
firm = Firm.scoped(:order => "id").first
assert_equal 2, firm.clients.scoped(:where => "#{QUOTED_TYPE} = 'Client'").all.length
@@ -1208,13 +1124,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [readers(:michael_welcome).id], posts(:welcome).readers_with_person_ids
end
- def test_get_ids_for_unloaded_finder_sql_associations_loads_them
- company = companies(:first_firm)
- assert !company.clients_using_sql.loaded?
- assert_equal [companies(:second_client).id], company.clients_using_sql_ids
- assert company.clients_using_sql.loaded?
- end
-
def test_get_ids_for_ordered_association
assert_equal [companies(:second_client).id, companies(:first_client).id], companies(:first_firm).clients_ordered_by_name_ids
end
@@ -1275,17 +1184,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert ! firm.clients.loaded?
end
- def test_include_loads_collection_if_target_uses_finder_sql
- firm = companies(:first_firm)
- client = firm.clients_using_sql.first
-
- firm.reload
- assert ! firm.clients_using_sql.loaded?
- assert firm.clients_using_sql.include?(client)
- assert firm.clients_using_sql.loaded?
- end
-
-
def test_include_returns_false_for_non_matching_record_to_verify_scoping
firm = companies(:first_firm)
client = Client.create!(:name => 'Not Associated')
@@ -1467,7 +1365,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_defining_has_many_association_with_delete_all_dependency_lazily_evaluates_target_class
ActiveRecord::Reflection::AssociationReflection.any_instance.expects(:class_name).never
- class_eval <<-EOF
+ class_eval(<<-EOF, __FILE__, __LINE__ + 1)
class DeleteAllModel < ActiveRecord::Base
has_many :nonentities, :dependent => :delete_all
end
@@ -1476,7 +1374,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_defining_has_many_association_with_nullify_dependency_lazily_evaluates_target_class
ActiveRecord::Reflection::AssociationReflection.any_instance.expects(:class_name).never
- class_eval <<-EOF
+ class_eval(<<-EOF, __FILE__, __LINE__ + 1)
class NullifyModel < ActiveRecord::Base
has_many :nonentities, :dependent => :nullify
end
@@ -1638,11 +1536,4 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
post.taggings_with_delete_all.delete_all
end
end
-
- test "association using a scope block" do
- author = authors(:david)
-
- assert author.posts.size > 1
- assert_equal author.posts.order('posts.id').limit(1), author.posts_with_scope_block
- end
end
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index 1c06007d86..805e9eac37 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -813,13 +813,6 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert post[:author_count].nil?
end
- def test_interpolated_conditions
- post = posts(:welcome)
- assert !post.tags.empty?
- assert_equal post.tags, post.interpolated_tags
- assert_equal post.tags, post.interpolated_tags_2
- end
-
def test_primary_key_option_on_source
post = posts(:welcome)
category = categories(:general)
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index fe385feb4a..aa0cdf5dfb 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -816,7 +816,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
def privatize(method_signature)
- @target.class_eval <<-private_method
+ @target.class_eval(<<-private_method, __FILE__, __LINE__ + 1)
private
def #{method_signature}
"I'm private"
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index e34f505a02..e4ba1c62c9 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1684,6 +1684,12 @@ class BasicsTest < ActiveRecord::TestCase
assert_kind_of String, Client.first.to_param
end
+ def test_to_param_returns_id_even_if_not_persisted
+ client = Client.new
+ client.id = 1
+ assert_equal "1", client.to_param
+ end
+
def test_inspect_class
assert_equal 'ActiveRecord::Base', ActiveRecord::Base.inspect
assert_equal 'LoosePerson(abstract)', LoosePerson.inspect
diff --git a/activerecord/test/cases/explain_test.rb b/activerecord/test/cases/explain_test.rb
index cb7781f8e7..bcc488f7ee 100644
--- a/activerecord/test/cases/explain_test.rb
+++ b/activerecord/test/cases/explain_test.rb
@@ -68,6 +68,16 @@ if ActiveRecord::Base.connection.supports_explain?
assert_equal [cars(:honda)], result
end
+ def test_logging_query_plan_when_counting_by_sql
+ base.logger.expects(:warn).with do |message|
+ message.starts_with?('EXPLAIN for:')
+ end
+
+ with_threshold(0) do
+ Car.count_by_sql "SELECT COUNT(*) FROM cars WHERE name = 'honda'"
+ end
+ end
+
def test_exec_explain_with_no_binds
sqls = %w(foo bar)
binds = [[], []]
diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb
index ce9be66069..ec4c554abb 100644
--- a/activerecord/test/cases/migration/change_schema_test.rb
+++ b/activerecord/test/cases/migration/change_schema_test.rb
@@ -141,8 +141,8 @@ module ActiveRecord
created_at_column = created_columns.detect {|c| c.name == 'created_at' }
updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
- assert !created_at_column.null
- assert !updated_at_column.null
+ assert created_at_column.null
+ assert updated_at_column.null
end
def test_create_table_with_timestamps_should_create_datetime_columns_with_options
diff --git a/activerecord/test/cases/migration/column_attributes_test.rb b/activerecord/test/cases/migration/column_attributes_test.rb
index 3014bbe273..9584d5dd06 100644
--- a/activerecord/test/cases/migration/column_attributes_test.rb
+++ b/activerecord/test/cases/migration/column_attributes_test.rb
@@ -54,13 +54,13 @@ module ActiveRecord
# Do a manual insertion
if current_adapter?(:OracleAdapter)
- connection.execute "insert into test_models (id, wealth, created_at, updated_at) values (people_seq.nextval, 12345678901234567890.0123456789, sysdate, sysdate)"
+ connection.execute "insert into test_models (id, wealth) values (people_seq.nextval, 12345678901234567890.0123456789)"
elsif current_adapter?(:OpenBaseAdapter) || (current_adapter?(:MysqlAdapter) && Mysql.client_version < 50003) #before mysql 5.0.3 decimals stored as strings
- connection.execute "insert into test_models (wealth, created_at, updated_at) values ('12345678901234567890.0123456789', 0, 0)"
+ connection.execute "insert into test_models (wealth) values ('12345678901234567890.0123456789')"
elsif current_adapter?(:PostgreSQLAdapter)
- connection.execute "insert into test_models (wealth, created_at, updated_at) values (12345678901234567890.0123456789, now(), now())"
+ connection.execute "insert into test_models (wealth) values (12345678901234567890.0123456789)"
else
- connection.execute "insert into test_models (wealth, created_at, updated_at) values (12345678901234567890.0123456789, 0, 0)"
+ connection.execute "insert into test_models (wealth) values (12345678901234567890.0123456789)"
end
# SELECT
diff --git a/activerecord/test/cases/migration/create_join_table_test.rb b/activerecord/test/cases/migration/create_join_table_test.rb
index 8b91b3bc92..cd1b0e8b47 100644
--- a/activerecord/test/cases/migration/create_join_table_test.rb
+++ b/activerecord/test/cases/migration/create_join_table_test.rb
@@ -42,22 +42,36 @@ module ActiveRecord
end
def test_create_join_table_with_the_table_name
- connection.create_join_table :artists, :musics, :table_name => :catalog
+ connection.create_join_table :artists, :musics, table_name: :catalog
assert_equal %w(artist_id music_id), connection.columns(:catalog).map(&:name).sort
end
def test_create_join_table_with_the_table_name_as_string
- connection.create_join_table :artists, :musics, :table_name => 'catalog'
+ connection.create_join_table :artists, :musics, table_name: 'catalog'
assert_equal %w(artist_id music_id), connection.columns(:catalog).map(&:name).sort
end
def test_create_join_table_with_column_options
- connection.create_join_table :artists, :musics, :column_options => {:null => true}
+ connection.create_join_table :artists, :musics, column_options: {null: true}
assert_equal [true, true], connection.columns(:artists_musics).map(&:null)
end
+
+ def test_create_join_table_without_indexes
+ connection.create_join_table :artists, :musics
+
+ assert connection.indexes(:artists_musics).blank?
+ end
+
+ def test_create_join_table_with_index
+ connection.create_join_table :artists, :musics do |t|
+ t.index [:artist_id, :music_id]
+ end
+
+ assert_equal [%w(artist_id music_id)], connection.indexes(:artists_musics).map(&:columns)
+ end
end
end
end
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index 67e0c155c8..51f07b6a2f 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -165,8 +165,8 @@ class ReflectionTest < ActiveRecord::TestCase
def test_reflection_of_all_associations
# FIXME these assertions bust a lot
- assert_equal 39, Firm.reflect_on_all_associations.size
- assert_equal 29, Firm.reflect_on_all_associations(:has_many).size
+ assert_equal 34, Firm.reflect_on_all_associations.size
+ assert_equal 24, Firm.reflect_on_all_associations(:has_many).size
assert_equal 10, Firm.reflect_on_all_associations(:has_one).size
assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size
end
diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb
index c173ee9a15..ea5e055289 100644
--- a/activerecord/test/cases/validations/uniqueness_validation_test.rb
+++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb
@@ -22,6 +22,14 @@ end
class Thaumaturgist < IneptWizard
end
+class ReplyTitle; end
+
+class ReplyWithTitleObject < Reply
+ validates_uniqueness_of :content, :scope => :title
+
+ def title; ReplyTitle.new; end
+end
+
class UniquenessValidationTest < ActiveRecord::TestCase
fixtures :topics, 'warehouse-things', :developers
@@ -104,6 +112,14 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert !r2.valid?, "Saving r2 first time"
end
+ def test_validate_uniqueness_with_composed_attribute_scope
+ r1 = ReplyWithTitleObject.create "title" => "r1", "content" => "hello world"
+ assert r1.valid?, "Saving r1"
+
+ r2 = ReplyWithTitleObject.create "title" => "r1", "content" => "hello world"
+ assert !r2.valid?, "Saving r2 first time"
+ end
+
def test_validate_uniqueness_with_object_arg
Reply.validates_uniqueness_of(:topic)
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index 6017178289..3157d8fe7f 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -1,12 +1,12 @@
class Author < ActiveRecord::Base
has_many :posts
has_many :very_special_comments, :through => :posts
- has_many :posts_with_comments, :include => :comments, :class_name => "Post"
- has_many :popular_grouped_posts, :include => :comments, :class_name => "Post", :group => "type", :having => "SUM(comments_count) > 1", :select => "type"
- has_many :posts_with_comments_sorted_by_comment_id, :include => :comments, :class_name => "Post", :order => 'comments.id'
- has_many :posts_sorted_by_id_limited, :class_name => "Post", :order => 'posts.id', :limit => 1
- has_many :posts_with_categories, :include => :categories, :class_name => "Post"
- has_many :posts_with_comments_and_categories, :include => [ :comments, :categories ], :order => "posts.id", :class_name => "Post"
+ has_many :posts_with_comments, -> { includes(:comments) }, :class_name => "Post"
+ has_many :popular_grouped_posts, -> { includes(:comments).group("type").having("SUM(comments_count) > 1").select("type") }, :class_name => "Post"
+ has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order('comments.id') }, :class_name => "Post"
+ has_many :posts_sorted_by_id_limited, -> { order('posts.id').limit(1) }, :class_name => "Post"
+ has_many :posts_with_categories, -> { includes(:categories) }, :class_name => "Post"
+ has_many :posts_with_comments_and_categories, -> { includes(:comments, :categories).order("posts.id") }, :class_name => "Post"
has_many :posts_containing_the_letter_a, :class_name => "Post"
has_many :posts_with_extension, :class_name => "Post" do #, :extend => ProxyTestExtension
def testing_proxy_owner
@@ -19,30 +19,28 @@ class Author < ActiveRecord::Base
proxy_target
end
end
- has_one :post_about_thinking, :class_name => 'Post', :conditions => "posts.title like '%thinking%'"
- has_one :post_about_thinking_with_last_comment, :class_name => 'Post', :conditions => "posts.title like '%thinking%'", :include => :last_comment
+ has_one :post_about_thinking, -> { where("posts.title like '%thinking%'") }, :class_name => 'Post'
+ has_one :post_about_thinking_with_last_comment, -> { where("posts.title like '%thinking%'").includes(:last_comment) }, :class_name => 'Post'
has_many :comments, :through => :posts
has_many :comments_containing_the_letter_e, :through => :posts, :source => :comments
- has_many :comments_with_order_and_conditions, :through => :posts, :source => :comments, :order => 'comments.body', :conditions => "comments.body like 'Thank%'"
- has_many :comments_with_include, :through => :posts, :source => :comments, :include => :post
-
- has_many :posts_with_scope_block, -> { order('posts.id').limit(1) }, :class_name => "Post"
+ has_many :comments_with_order_and_conditions, -> { order('comments.body').where("comments.body like 'Thank%'") }, :through => :posts, :source => :comments
+ has_many :comments_with_include, -> { includes(:post) }, :through => :posts, :source => :comments
has_many :first_posts
- has_many :comments_on_first_posts, :through => :first_posts, :source => :comments, :order => 'posts.id desc, comments.id asc'
+ has_many :comments_on_first_posts, -> { order('posts.id desc, comments.id asc') }, :through => :first_posts, :source => :comments
has_one :first_post
- has_one :comment_on_first_post, :through => :first_post, :source => :comments, :order => 'posts.id desc, comments.id asc'
+ has_one :comment_on_first_post, -> { order('posts.id desc, comments.id asc') }, :through => :first_post, :source => :comments
- has_many :thinking_posts, :class_name => 'Post', :conditions => { :title => 'So I was thinking' }, :dependent => :delete_all
- has_many :welcome_posts, :class_name => 'Post', :conditions => { :title => 'Welcome to the weblog' }
+ has_many :thinking_posts, -> { where(:title => 'So I was thinking') }, :dependent => :delete_all, :class_name => 'Post'
+ has_many :welcome_posts, -> { where(:title => 'Welcome to the weblog') }, :class_name => 'Post'
- has_many :comments_desc, :through => :posts, :source => :comments, :order => 'comments.id DESC'
- has_many :limited_comments, :through => :posts, :source => :comments, :limit => 1
+ has_many :comments_desc, -> { order('comments.id DESC') }, :through => :posts, :source => :comments
+ has_many :limited_comments, -> { limit(1) }, :through => :posts, :source => :comments
has_many :funky_comments, :through => :posts, :source => :comments
- has_many :ordered_uniq_comments, :through => :posts, :source => :comments, :uniq => true, :order => 'comments.id'
- has_many :ordered_uniq_comments_desc, :through => :posts, :source => :comments, :uniq => true, :order => 'comments.id DESC'
- has_many :readonly_comments, :through => :posts, :source => :comments, :readonly => true
+ has_many :ordered_uniq_comments, -> { uniq.order('comments.id') }, :through => :posts, :source => :comments
+ has_many :ordered_uniq_comments_desc, -> { uniq.order('comments.id DESC') }, :through => :posts, :source => :comments
+ has_many :readonly_comments, -> { readonly }, :through => :posts, :source => :comments
has_many :special_posts
has_many :special_post_comments, :through => :special_posts, :source => :comments
@@ -50,16 +48,15 @@ class Author < ActiveRecord::Base
has_many :sti_posts, :class_name => 'StiPost'
has_many :sti_post_comments, :through => :sti_posts, :source => :comments
- has_many :special_nonexistant_posts, :class_name => "SpecialPost", :conditions => "posts.body = 'nonexistant'"
- has_many :special_nonexistant_post_comments, :through => :special_nonexistant_posts, :source => :comments, :conditions => { 'comments.post_id' => 0 }
+ has_many :special_nonexistant_posts, -> { where("posts.body = 'nonexistant'") }, :class_name => "SpecialPost"
+ has_many :special_nonexistant_post_comments, -> { where('comments.post_id' => 0) }, :through => :special_nonexistant_posts, :source => :comments
has_many :nonexistant_comments, :through => :posts
- has_many :hello_posts, :class_name => "Post", :conditions => "posts.body = 'hello'"
+ has_many :hello_posts, -> { where "posts.body = 'hello'" }, :class_name => "Post"
has_many :hello_post_comments, :through => :hello_posts, :source => :comments
- has_many :posts_with_no_comments, :class_name => 'Post', :conditions => { 'comments.id' => nil }, :include => :comments
+ has_many :posts_with_no_comments, -> { where('comments.id' => nil).includes(:comments) }, :class_name => 'Post'
- has_many :hello_posts_with_hash_conditions, :class_name => "Post",
-:conditions => {:body => 'hello'}
+ has_many :hello_posts_with_hash_conditions, -> { where(:body => 'hello') }, :class_name => "Post"
has_many :hello_post_comments_with_hash_conditions, :through =>
:hello_posts_with_hash_conditions, :source => :comments
@@ -86,29 +83,31 @@ class Author < ActiveRecord::Base
has_many :special_categories, :through => :special_categorizations, :source => :category
has_one :special_category, :through => :special_categorizations, :source => :category
- has_many :categories_like_general, :through => :categorizations, :source => :category, :class_name => 'Category', :conditions => { :name => 'General' }
+ has_many :categories_like_general, -> { where(:name => 'General') }, :through => :categorizations, :source => :category, :class_name => 'Category'
has_many :categorized_posts, :through => :categorizations, :source => :post
- has_many :unique_categorized_posts, :through => :categorizations, :source => :post, :uniq => true
+ has_many :unique_categorized_posts, -> { uniq }, :through => :categorizations, :source => :post
has_many :nothings, :through => :kateggorisatons, :class_name => 'Category'
has_many :author_favorites
- has_many :favorite_authors, :through => :author_favorites, :order => 'name'
+ has_many :favorite_authors, -> { order('name') }, :through => :author_favorites
has_many :tagging, :through => :posts
has_many :taggings, :through => :posts
has_many :tags, :through => :posts
- has_many :similar_posts, :through => :tags, :source => :tagged_posts, :uniq => true
- has_many :distinct_tags, :through => :posts, :source => :tags, :select => "DISTINCT tags.*", :order => "tags.name"
has_many :post_categories, :through => :posts, :source => :categories
has_many :tagging_tags, :through => :taggings, :source => :tag
+
+ has_many :similar_posts, -> { uniq }, :through => :tags, :source => :tagged_posts
+ has_many :distinct_tags, -> { select("DISTINCT tags.*").order("tags.name") }, :through => :posts, :source => :tags
+
has_many :tags_with_primary_key, :through => :posts
has_many :books
has_many :subscriptions, :through => :books
- has_many :subscribers, :through => :subscriptions, :order => "subscribers.nick" # through has_many :through (on through reflection)
- has_many :distinct_subscribers, :through => :subscriptions, :source => :subscriber, :select => "DISTINCT subscribers.*", :order => "subscribers.nick"
+ has_many :subscribers, -> { order("subscribers.nick") }, :through => :subscriptions
+ has_many :distinct_subscribers, -> { select("DISTINCT subscribers.*").order("subscribers.nick") }, :through => :subscriptions, :source => :subscriber
has_one :essay, :primary_key => :name, :as => :writer
has_one :essay_category, :through => :essay, :source => :category
@@ -132,12 +131,11 @@ class Author < ActiveRecord::Base
has_many :category_post_comments, :through => :categories, :source => :post_comments
- has_many :misc_posts, :class_name => 'Post',
- :conditions => { :posts => { :title => ['misc post by bob', 'misc post by mary'] } }
+ has_many :misc_posts, -> { where(:posts => { :title => ['misc post by bob', 'misc post by mary'] }) }, :class_name => 'Post'
has_many :misc_post_first_blue_tags, :through => :misc_posts, :source => :first_blue_tags
- has_many :misc_post_first_blue_tags_2, :through => :posts, :source => :first_blue_tags_2,
- :conditions => { :posts => { :title => ['misc post by bob', 'misc post by mary'] } }
+ has_many :misc_post_first_blue_tags_2, -> { where(:posts => { :title => ['misc post by bob', 'misc post by mary'] }) },
+ :through => :posts, :source => :first_blue_tags_2
has_many :posts_with_default_include, :class_name => 'PostWithDefaultInclude'
has_many :comments_on_posts_with_default_include, :through => :posts_with_default_include, :source => :comments
diff --git a/activerecord/test/models/book.rb b/activerecord/test/models/book.rb
index d27d0af77c..ce81a37966 100644
--- a/activerecord/test/models/book.rb
+++ b/activerecord/test/models/book.rb
@@ -2,7 +2,7 @@ class Book < ActiveRecord::Base
has_many :authors
has_many :citations, :foreign_key => 'book1_id'
- has_many :references, :through => :citations, :source => :reference_of, :uniq => true
+ has_many :references, -> { uniq }, :through => :citations, :source => :reference_of
has_many :subscriptions
has_many :subscribers, :through => :subscriptions
diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb
index b4bc0ad5fa..ac42f444e1 100644
--- a/activerecord/test/models/car.rb
+++ b/activerecord/test/models/car.rb
@@ -1,11 +1,11 @@
class Car < ActiveRecord::Base
has_many :bulbs
- has_many :foo_bulbs, :class_name => "Bulb", :conditions => { :name => 'foo' }
- has_many :frickinawesome_bulbs, :class_name => "Bulb", :conditions => { :frickinawesome => true }
+ has_many :foo_bulbs, -> { where(:name => 'foo') }, :class_name => "Bulb"
+ has_many :frickinawesome_bulbs, -> { where :frickinawesome => true }, :class_name => "Bulb"
has_one :bulb
- has_one :frickinawesome_bulb, :class_name => "Bulb", :conditions => { :frickinawesome => true }
+ has_one :frickinawesome_bulb, -> { where :frickinawesome => true }, :class_name => "Bulb"
has_many :tyres
has_many :engines, :dependent => :destroy
diff --git a/activerecord/test/models/category.rb b/activerecord/test/models/category.rb
index ab3139680c..f8c8ebb70c 100644
--- a/activerecord/test/models/category.rb
+++ b/activerecord/test/models/category.rb
@@ -2,20 +2,20 @@ class Category < ActiveRecord::Base
has_and_belongs_to_many :posts
has_and_belongs_to_many :special_posts, :class_name => "Post"
has_and_belongs_to_many :other_posts, :class_name => "Post"
- has_and_belongs_to_many :posts_with_authors_sorted_by_author_id, :class_name => "Post", :include => :authors, :order => "authors.id"
+ has_and_belongs_to_many :posts_with_authors_sorted_by_author_id, -> { includes(:authors).order("authors.id") }, :class_name => "Post"
- has_and_belongs_to_many(:select_testing_posts,
+ has_and_belongs_to_many :select_testing_posts,
+ -> { select 'posts.*, 1 as correctness_marker' },
:class_name => 'Post',
:foreign_key => 'category_id',
- :association_foreign_key => 'post_id',
- :select => 'posts.*, 1 as correctness_marker')
+ :association_foreign_key => 'post_id'
has_and_belongs_to_many :post_with_conditions,
- :class_name => 'Post',
- :conditions => { :title => 'Yet Another Testing Title' }
+ -> { where :title => 'Yet Another Testing Title' },
+ :class_name => 'Post'
- has_and_belongs_to_many :popular_grouped_posts, :class_name => "Post", :group => "posts.type", :having => "sum(comments.post_id) > 2", :include => :comments
- has_and_belongs_to_many :posts_grouped_by_title, :class_name => "Post", :group => "title", :select => "title"
+ has_and_belongs_to_many :popular_grouped_posts, -> { group("posts.type").having("sum(comments.post_id) > 2").includes(:comments) }, :class_name => "Post"
+ has_and_belongs_to_many :posts_grouped_by_title, -> { group("title").select("title") }, :class_name => "Post"
def self.what_are_you
'a category...'
@@ -25,7 +25,7 @@ class Category < ActiveRecord::Base
has_many :post_comments, :through => :posts, :source => :comments
has_many :authors, :through => :categorizations
- has_many :authors_with_select, :through => :categorizations, :source => :author, :select => 'authors.*, categorizations.post_id'
+ has_many :authors_with_select, -> { select 'authors.*, categorizations.post_id' }, :through => :categorizations, :source => :author
scope :general, -> { where(:name => 'General') }
end
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index 7b993d5a2c..ff6b7d085f 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -36,60 +36,47 @@ module Namespaced
end
class Firm < Company
- has_many :clients, :order => "id", :dependent => :destroy, :counter_sql =>
- "SELECT COUNT(*) FROM companies WHERE firm_id = 1 " +
- "AND (#{QUOTED_TYPE} = 'Client' OR #{QUOTED_TYPE} = 'SpecialClient' OR #{QUOTED_TYPE} = 'VerySpecialClient' )",
+ has_many :clients, -> { order "id" }, :dependent => :destroy,
:before_remove => :log_before_remove,
:after_remove => :log_after_remove
has_many :unsorted_clients, :class_name => "Client"
has_many :unsorted_clients_with_symbol, :class_name => :Client
- has_many :clients_sorted_desc, :class_name => "Client", :order => "id DESC"
- has_many :clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id"
- has_many :clients_ordered_by_name, :order => "name", :class_name => "Client"
+ has_many :clients_sorted_desc, -> { order "id DESC" }, :class_name => "Client"
+ has_many :clients_of_firm, -> { order "id" }, :foreign_key => "client_of", :class_name => "Client"
+ has_many :clients_ordered_by_name, -> { order "name" }, :class_name => "Client"
has_many :unvalidated_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :validate => false
- has_many :dependent_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :destroy
- has_many :exclusively_dependent_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all
- has_many :limited_clients, :class_name => "Client", :limit => 1
- has_many :clients_with_interpolated_conditions, :class_name => "Client", :conditions => proc { "rating > #{rating}" }
- has_many :clients_like_ms, :conditions => "name = 'Microsoft'", :class_name => "Client", :order => "id"
- has_many :clients_like_ms_with_hash_conditions, :conditions => { :name => 'Microsoft' }, :class_name => "Client", :order => "id"
- has_many :clients_using_sql, :class_name => "Client", :finder_sql => proc { "SELECT * FROM companies WHERE client_of = #{id}" }
- has_many :clients_using_counter_sql, :class_name => "Client",
- :finder_sql => proc { "SELECT * FROM companies WHERE client_of = #{id} " },
- :counter_sql => proc { "SELECT COUNT(*) FROM companies WHERE client_of = #{id}" }
- has_many :clients_using_zero_counter_sql, :class_name => "Client",
- :finder_sql => proc { "SELECT * FROM companies WHERE client_of = #{id}" },
- :counter_sql => proc { "SELECT 0 FROM companies WHERE client_of = #{id}" }
- has_many :no_clients_using_counter_sql, :class_name => "Client",
- :finder_sql => 'SELECT * FROM companies WHERE client_of = 1000',
- :counter_sql => 'SELECT COUNT(*) FROM companies WHERE client_of = 1000'
- has_many :clients_using_finder_sql, :class_name => "Client", :finder_sql => 'SELECT * FROM companies WHERE 1=1'
+ has_many :dependent_clients_of_firm, -> { order "id" }, :foreign_key => "client_of", :class_name => "Client", :dependent => :destroy
+ has_many :exclusively_dependent_clients_of_firm, -> { order "id" }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
+ has_many :limited_clients, -> { limit 1 }, :class_name => "Client"
+ has_many :clients_with_interpolated_conditions, ->(firm) { where "rating > #{firm.rating}" }, :class_name => "Client"
+ has_many :clients_like_ms, -> { where("name = 'Microsoft'").order("id") }, :class_name => "Client"
+ has_many :clients_like_ms_with_hash_conditions, -> { where(:name => 'Microsoft').order("id") }, :class_name => "Client"
has_many :plain_clients, :class_name => 'Client'
- has_many :readonly_clients, :class_name => 'Client', :readonly => true
+ has_many :readonly_clients, -> { readonly }, :class_name => 'Client'
has_many :clients_using_primary_key, :class_name => 'Client',
:primary_key => 'name', :foreign_key => 'firm_name'
has_many :clients_using_primary_key_with_delete_all, :class_name => 'Client',
:primary_key => 'name', :foreign_key => 'firm_name', :dependent => :delete_all
- has_many :clients_grouped_by_firm_id, :class_name => "Client", :group => "firm_id", :select => "firm_id"
- has_many :clients_grouped_by_name, :class_name => "Client", :group => "name", :select => "name"
+ has_many :clients_grouped_by_firm_id, -> { group("firm_id").select("firm_id") }, :class_name => "Client"
+ has_many :clients_grouped_by_name, -> { group("name").select("name") }, :class_name => "Client"
has_one :account, :foreign_key => "firm_id", :dependent => :destroy, :validate => true
has_one :unvalidated_account, :foreign_key => "firm_id", :class_name => 'Account', :validate => false
- has_one :account_with_select, :foreign_key => "firm_id", :select => "id, firm_id", :class_name=>'Account'
- has_one :readonly_account, :foreign_key => "firm_id", :class_name => "Account", :readonly => true
+ has_one :account_with_select, -> { select("id, firm_id") }, :foreign_key => "firm_id", :class_name=>'Account'
+ has_one :readonly_account, -> { readonly }, :foreign_key => "firm_id", :class_name => "Account"
# added order by id as in fixtures there are two accounts for Rails Core
# Oracle tests were failing because of that as the second fixture was selected
- has_one :account_using_primary_key, :primary_key => "firm_id", :class_name => "Account", :order => "id"
+ has_one :account_using_primary_key, -> { order('id') }, :primary_key => "firm_id", :class_name => "Account"
has_one :account_using_foreign_and_primary_keys, :foreign_key => "firm_name", :primary_key => "name", :class_name => "Account"
has_one :deletable_account, :foreign_key => "firm_id", :class_name => "Account", :dependent => :delete
- has_one :account_limit_500_with_hash_conditions, :foreign_key => "firm_id", :class_name => "Account", :conditions => { :credit_limit => 500 }
+ has_one :account_limit_500_with_hash_conditions, -> { where :credit_limit => 500 }, :foreign_key => "firm_id", :class_name => "Account"
has_one :unautosaved_account, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false
has_many :accounts
has_many :unautosaved_accounts, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false
- has_many :association_with_references, :class_name => 'Client', :references => :foo
+ has_many :association_with_references, -> { references(:foo) }, :class_name => 'Client'
def log
@log ||= []
@@ -111,20 +98,20 @@ class DependentFirm < Company
end
class RestrictedFirm < Company
- has_one :account, :foreign_key => "firm_id", :dependent => :restrict, :order => "id"
- has_many :companies, :foreign_key => 'client_of', :order => "id", :dependent => :restrict
+ has_one :account, -> { order("id") }, :foreign_key => "firm_id", :dependent => :restrict
+ has_many :companies, -> { order("id") }, :foreign_key => 'client_of', :dependent => :restrict
end
class Client < Company
belongs_to :firm, :foreign_key => "client_of"
belongs_to :firm_with_basic_id, :class_name => "Firm", :foreign_key => "firm_id"
- belongs_to :firm_with_select, :class_name => "Firm", :foreign_key => "firm_id", :select => "id"
+ belongs_to :firm_with_select, -> { select("id") }, :class_name => "Firm", :foreign_key => "firm_id"
belongs_to :firm_with_other_name, :class_name => "Firm", :foreign_key => "client_of"
- belongs_to :firm_with_condition, :class_name => "Firm", :foreign_key => "client_of", :conditions => ["1 = ?", 1]
+ belongs_to :firm_with_condition, -> { where "1 = ?", 1 }, :class_name => "Firm", :foreign_key => "client_of"
belongs_to :firm_with_primary_key, :class_name => "Firm", :primary_key => "name", :foreign_key => "firm_name"
belongs_to :firm_with_primary_key_symbols, :class_name => "Firm", :primary_key => :name, :foreign_key => :firm_name
- belongs_to :readonly_firm, :class_name => "Firm", :foreign_key => "firm_id", :readonly => true
- belongs_to :bob_firm, :class_name => "Firm", :foreign_key => "client_of", :conditions => { :name => "Bob" }
+ belongs_to :readonly_firm, -> { readonly }, :class_name => "Firm", :foreign_key => "firm_id"
+ belongs_to :bob_firm, -> { where :name => "Bob" }, :class_name => "Firm", :foreign_key => "client_of"
has_many :accounts, :through => :firm
belongs_to :account
@@ -179,9 +166,9 @@ end
class ExclusivelyDependentFirm < Company
has_one :account, :foreign_key => "firm_id", :dependent => :delete
- has_many :dependent_sanitized_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => "name = 'BigShot Inc.'"
- has_many :dependent_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => ["name = ?", 'BigShot Inc.']
- has_many :dependent_hash_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => {:name => 'BigShot Inc.'}
+ has_many :dependent_sanitized_conditional_clients_of_firm, -> { order("id").where("name = 'BigShot Inc.'") }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
+ has_many :dependent_conditional_clients_of_firm, -> { order("id").where("name = ?", 'BigShot Inc.') }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
+ has_many :dependent_hash_conditional_clients_of_firm, -> { order("id").where(:name => 'BigShot Inc.') }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
end
class SpecialClient < Client
diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb
index 2c8c30efb4..0d14fa1be1 100644
--- a/activerecord/test/models/company_in_module.rb
+++ b/activerecord/test/models/company_in_module.rb
@@ -7,11 +7,10 @@ module MyApplication
end
class Firm < Company
- has_many :clients, :order => "id", :dependent => :destroy
- has_many :clients_sorted_desc, :class_name => "Client", :order => "id DESC"
- has_many :clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id"
- has_many :clients_like_ms, :conditions => "name = 'Microsoft'", :class_name => "Client", :order => "id"
- has_many :clients_using_sql, :class_name => "Client", :finder_sql => 'SELECT * FROM companies WHERE client_of = #{id}'
+ has_many :clients, -> { order("id") }, :dependent => :destroy
+ has_many :clients_sorted_desc, -> { order("id DESC") }, :class_name => "Client"
+ has_many :clients_of_firm, -> { order "id" }, :foreign_key => "client_of", :class_name => "Client"
+ has_many :clients_like_ms, -> { where("name = 'Microsoft'").order("id") }, :class_name => "Client"
has_one :account, :class_name => 'MyApplication::Billing::Account', :dependent => :destroy
end
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index 43cde4ab73..1caf8fca53 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -20,22 +20,22 @@ class Developer < ActiveRecord::Base
end
has_and_belongs_to_many :projects_extended_by_name,
+ -> { extending(DeveloperProjectsAssociationExtension) },
:class_name => "Project",
:join_table => "developers_projects",
- :association_foreign_key => "project_id",
- :extend => DeveloperProjectsAssociationExtension
+ :association_foreign_key => "project_id"
has_and_belongs_to_many :projects_extended_by_name_twice,
+ -> { extending(DeveloperProjectsAssociationExtension, DeveloperProjectsAssociationExtension2) },
:class_name => "Project",
:join_table => "developers_projects",
- :association_foreign_key => "project_id",
- :extend => [DeveloperProjectsAssociationExtension, DeveloperProjectsAssociationExtension2]
+ :association_foreign_key => "project_id"
has_and_belongs_to_many :projects_extended_by_name_and_block,
+ -> { extending(DeveloperProjectsAssociationExtension) },
:class_name => "Project",
:join_table => "developers_projects",
- :association_foreign_key => "project_id",
- :extend => DeveloperProjectsAssociationExtension do
+ :association_foreign_key => "project_id" do
def find_least_recent
scoped(:order => "id ASC").first
end
@@ -175,14 +175,14 @@ end
class EagerDeveloperWithDefaultScope < ActiveRecord::Base
self.table_name = 'developers'
- has_and_belongs_to_many :projects, :foreign_key => 'developer_id', :join_table => 'developers_projects', :order => 'projects.id'
+ has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
default_scope { includes(:projects) }
end
class EagerDeveloperWithClassMethodDefaultScope < ActiveRecord::Base
self.table_name = 'developers'
- has_and_belongs_to_many :projects, :foreign_key => 'developer_id', :join_table => 'developers_projects', :order => 'projects.id'
+ has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
def self.default_scope
includes(:projects)
@@ -191,21 +191,21 @@ end
class EagerDeveloperWithLambdaDefaultScope < ActiveRecord::Base
self.table_name = 'developers'
- has_and_belongs_to_many :projects, :foreign_key => 'developer_id', :join_table => 'developers_projects', :order => 'projects.id'
+ has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
default_scope lambda { includes(:projects) }
end
class EagerDeveloperWithBlockDefaultScope < ActiveRecord::Base
self.table_name = 'developers'
- has_and_belongs_to_many :projects, :foreign_key => 'developer_id', :join_table => 'developers_projects', :order => 'projects.id'
+ has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
default_scope { includes(:projects) }
end
class EagerDeveloperWithCallableDefaultScope < ActiveRecord::Base
self.table_name = 'developers'
- has_and_belongs_to_many :projects, :foreign_key => 'developer_id', :join_table => 'developers_projects', :order => 'projects.id'
+ has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
default_scope OpenStruct.new(:call => includes(:projects))
end
diff --git a/activerecord/test/models/liquid.rb b/activerecord/test/models/liquid.rb
index 3fcd5e4b69..6cfd443e75 100644
--- a/activerecord/test/models/liquid.rb
+++ b/activerecord/test/models/liquid.rb
@@ -1,5 +1,5 @@
class Liquid < ActiveRecord::Base
self.table_name = :liquid
- has_many :molecules, :uniq => true
+ has_many :molecules, -> { uniq }
end
diff --git a/activerecord/test/models/member.rb b/activerecord/test/models/member.rb
index 1f719b0858..359b29fac3 100644
--- a/activerecord/test/models/member.rb
+++ b/activerecord/test/models/member.rb
@@ -5,8 +5,8 @@ class Member < ActiveRecord::Base
has_many :fellow_members, :through => :club, :source => :members
has_one :club, :through => :current_membership
has_one :selected_club, :through => :selected_membership, :source => :club
- has_one :favourite_club, :through => :membership, :conditions => ["memberships.favourite = ?", true], :source => :club
- has_one :hairy_club, :through => :membership, :conditions => {:clubs => {:name => "Moustache and Eyebrow Fancier Club"}}, :source => :club
+ has_one :favourite_club, -> { where "memberships.favourite = ?", true }, :through => :membership, :source => :club
+ has_one :hairy_club, -> { where :clubs => {:name => "Moustache and Eyebrow Fancier Club"} }, :through => :membership, :source => :club
has_one :sponsor, :as => :sponsorable
has_one :sponsor_club, :through => :sponsor
has_one :member_detail
@@ -27,7 +27,7 @@ class Member < ActiveRecord::Base
has_many :current_memberships
has_one :club_through_many, :through => :current_memberships, :source => :club
- has_many :current_memberships, :conditions => { :favourite => true }
+ has_many :current_memberships, -> { where :favourite => true }
has_many :clubs, :through => :current_memberships
end
diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb
index 33cd6020a1..e204508986 100644
--- a/activerecord/test/models/person.rb
+++ b/activerecord/test/models/person.rb
@@ -5,14 +5,14 @@ class Person < ActiveRecord::Base
has_many :posts, :through => :readers
has_many :secure_posts, :through => :secure_readers
- has_many :posts_with_no_comments, :through => :readers, :source => :post, :include => :comments,
- :conditions => 'comments.id is null', :references => :comments
+ has_many :posts_with_no_comments, -> { includes(:comments).where('comments.id is null').references(:comments) },
+ :through => :readers, :source => :post
has_many :references
has_many :bad_references
- has_many :fixed_bad_references, :conditions => { :favourite => true }, :class_name => 'BadReference'
- has_one :favourite_reference, :class_name => 'Reference', :conditions => ['favourite=?', true]
- has_many :posts_with_comments_sorted_by_comment_id, :through => :readers, :source => :post, :include => :comments, :order => 'comments.id'
+ has_many :fixed_bad_references, -> { where :favourite => true }, :class_name => 'BadReference'
+ has_one :favourite_reference, -> { where 'favourite=?', true }, :class_name => 'Reference'
+ has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order('comments.id') }, :through => :readers, :source => :post
has_many :jobs, :through => :references
has_many :jobs_with_dependent_destroy, :source => :job, :through => :references, :dependent => :destroy
@@ -109,4 +109,4 @@ class NestedPerson < ActiveRecord::Base
def best_friend_first_name=(new_name)
assign_attributes({ :best_friend_attributes => { :first_name => new_name } })
end
-end \ No newline at end of file
+end
diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb
index 5e0f5323e6..609b9369a9 100644
--- a/activerecord/test/models/pirate.rb
+++ b/activerecord/test/models/pirate.rb
@@ -1,7 +1,7 @@
class Pirate < ActiveRecord::Base
belongs_to :parrot, :validate => true
belongs_to :non_validated_parrot, :class_name => 'Parrot'
- has_and_belongs_to_many :parrots, :validate => true, :order => 'parrots.id ASC'
+ has_and_belongs_to_many :parrots, -> { order('parrots.id ASC') }, :validate => true
has_and_belongs_to_many :non_validated_parrots, :class_name => 'Parrot'
has_and_belongs_to_many :parrots_with_method_callbacks, :class_name => "Parrot",
:before_add => :log_before_add,
@@ -21,7 +21,7 @@ class Pirate < ActiveRecord::Base
has_one :ship
has_one :update_only_ship, :class_name => 'Ship'
has_one :non_validated_ship, :class_name => 'Ship'
- has_many :birds, :order => 'birds.id ASC'
+ has_many :birds, -> { order('birds.id ASC') }
has_many :birds_with_method_callbacks, :class_name => "Bird",
:before_add => :log_before_add,
:after_add => :log_after_add,
@@ -34,7 +34,7 @@ class Pirate < ActiveRecord::Base
:after_remove => proc {|p,b| p.ship_log << "after_removing_proc_bird_#{b.id}"}
has_many :birds_with_reject_all_blank, :class_name => "Bird"
- has_one :foo_bulb, :foreign_key => :car_id, :class_name => "Bulb", :conditions => { :name => 'foo' }
+ has_one :foo_bulb, -> { where :name => 'foo' }, :foreign_key => :car_id, :class_name => "Bulb"
accepts_nested_attributes_for :parrots, :birds, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 1aaf9a1b82..92038c76e5 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -16,14 +16,14 @@ class Post < ActiveRecord::Base
end
end
- belongs_to :author_with_posts, :class_name => "Author", :foreign_key => :author_id, :include => :posts
- belongs_to :author_with_address, :class_name => "Author", :foreign_key => :author_id, :include => :author_address
+ belongs_to :author_with_posts, -> { includes(:posts) }, :class_name => "Author", :foreign_key => :author_id
+ belongs_to :author_with_address, -> { includes(:author_address) }, :class_name => "Author", :foreign_key => :author_id
def first_comment
super.body
end
- has_one :first_comment, :class_name => 'Comment', :order => 'id ASC'
- has_one :last_comment, :class_name => 'Comment', :order => 'id desc'
+ has_one :first_comment, -> { order('id ASC') }, :class_name => 'Comment'
+ has_one :last_comment, -> { order('id desc') }, :class_name => 'Comment'
scope :with_special_comments, -> { joins(:comments).where(:comments => {:type => 'SpecialComment'}) }
scope :with_very_special_comments, -> { joins(:comments).where(:comments => {:type => 'VerySpecialComment'}) }
@@ -47,13 +47,14 @@ class Post < ActiveRecord::Base
has_many :author_categorizations, :through => :author, :source => :categorizations
has_many :author_addresses, :through => :author
- has_many :comments_with_interpolated_conditions, :class_name => 'Comment',
- :conditions => proc { ["#{"#{aliased_table_name}." rescue ""}body = ?", 'Thank you for the welcome'] }
+ has_many :comments_with_interpolated_conditions,
+ ->(p) { where "#{"#{p.aliased_table_name}." rescue ""}body = ?", 'Thank you for the welcome' },
+ :class_name => 'Comment'
has_one :very_special_comment
- has_one :very_special_comment_with_post, :class_name => "VerySpecialComment", :include => :post
+ has_one :very_special_comment_with_post, -> { includes(:post) }, :class_name => "VerySpecialComment"
has_many :special_comments
- has_many :nonexistant_comments, :class_name => 'Comment', :conditions => 'comments.id < 0'
+ has_many :nonexistant_comments, -> { where 'comments.id < 0' }, :class_name => 'Comment'
has_many :special_comments_ratings, :through => :special_comments, :source => :ratings
has_many :special_comments_ratings_taggings, :through => :special_comments_ratings, :source => :taggings
@@ -69,28 +70,24 @@ class Post < ActiveRecord::Base
end
end
- has_many :interpolated_taggings, :class_name => 'Tagging', :as => :taggable, :conditions => proc { "1 = #{1}" }
- has_many :interpolated_tags, :through => :taggings
- has_many :interpolated_tags_2, :through => :interpolated_taggings, :source => :tag
-
has_many :taggings_with_delete_all, :class_name => 'Tagging', :as => :taggable, :dependent => :delete_all
has_many :taggings_with_destroy, :class_name => 'Tagging', :as => :taggable, :dependent => :destroy
has_many :tags_with_destroy, :through => :taggings, :source => :tag, :dependent => :destroy
has_many :tags_with_nullify, :through => :taggings, :source => :tag, :dependent => :nullify
- has_many :misc_tags, :through => :taggings, :source => :tag, :conditions => { :tags => { :name => 'Misc' } }
+ has_many :misc_tags, -> { where :tags => { :name => 'Misc' } }, :through => :taggings, :source => :tag
has_many :funky_tags, :through => :taggings, :source => :tag
has_many :super_tags, :through => :taggings
has_many :tags_with_primary_key, :through => :taggings, :source => :tag_with_primary_key
has_one :tagging, :as => :taggable
- has_many :first_taggings, :as => :taggable, :class_name => 'Tagging', :conditions => { :taggings => { :comment => 'first' } }
- has_many :first_blue_tags, :through => :first_taggings, :source => :tag, :conditions => { :tags => { :name => 'Blue' } }
+ has_many :first_taggings, -> { where :taggings => { :comment => 'first' } }, :as => :taggable, :class_name => 'Tagging'
+ has_many :first_blue_tags, -> { where :tags => { :name => 'Blue' } }, :through => :first_taggings, :source => :tag
- has_many :first_blue_tags_2, :through => :taggings, :source => :blue_tag, :conditions => { :taggings => { :comment => 'first' } }
+ has_many :first_blue_tags_2, -> { where :taggings => { :comment => 'first' } }, :through => :taggings, :source => :blue_tag
- has_many :invalid_taggings, :as => :taggable, :class_name => "Tagging", :conditions => 'taggings.id < 0'
+ has_many :invalid_taggings, -> { where 'taggings.id < 0' }, :as => :taggable, :class_name => "Tagging"
has_many :invalid_tags, :through => :invalid_taggings, :source => :tag
has_many :categorizations, :foreign_key => :category_id
@@ -109,7 +106,7 @@ class Post < ActiveRecord::Base
has_many :readers
has_many :secure_readers
- has_many :readers_with_person, :include => :person, :class_name => "Reader"
+ has_many :readers_with_person, -> { includes(:person) }, :class_name => "Reader"
has_many :people, :through => :readers
has_many :secure_people, :through => :secure_readers
has_many :single_people, :through => :readers
@@ -118,7 +115,7 @@ class Post < ActiveRecord::Base
:after_add => lambda {|owner, reader| log(:added, :after, reader.first_name) },
:before_remove => lambda {|owner, reader| log(:removed, :before, reader.first_name) },
:after_remove => lambda {|owner, reader| log(:removed, :after, reader.first_name) }
- has_many :skimmers, :class_name => 'Reader', :conditions => { :skimmer => true }
+ has_many :skimmers, -> { where :skimmer => true }, :class_name => 'Reader'
has_many :impatient_people, :through => :skimmers, :source => :person
def self.top(limit)
diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb
index 32ce164995..c31750688b 100644
--- a/activerecord/test/models/project.rb
+++ b/activerecord/test/models/project.rb
@@ -1,26 +1,17 @@
class Project < ActiveRecord::Base
- has_and_belongs_to_many :developers, :uniq => true, :order => 'developers.name desc, developers.id desc'
- has_and_belongs_to_many :readonly_developers, :class_name => "Developer", :readonly => true
- has_and_belongs_to_many :selected_developers, :class_name => "Developer", :select => "developers.*", :uniq => true
- has_and_belongs_to_many :non_unique_developers, :order => 'developers.name desc, developers.id desc', :class_name => 'Developer'
- has_and_belongs_to_many :limited_developers, :class_name => "Developer", :limit => 1
- has_and_belongs_to_many :developers_named_david, :class_name => "Developer", :conditions => "name = 'David'", :uniq => true
- has_and_belongs_to_many :developers_named_david_with_hash_conditions, :class_name => "Developer", :conditions => { :name => 'David' }, :uniq => true
- has_and_belongs_to_many :salaried_developers, :class_name => "Developer", :conditions => "salary > 0"
- has_and_belongs_to_many :developers_with_finder_sql, :class_name => "Developer", :finder_sql => proc { "SELECT t.*, j.* FROM developers_projects j, developers t WHERE t.id = j.developer_id AND j.project_id = #{id} ORDER BY t.id" }
- has_and_belongs_to_many :developers_with_multiline_finder_sql, :class_name => "Developer", :finder_sql => proc {
- "SELECT
- t.*, j.*
- FROM
- developers_projects j,
- developers t WHERE t.id = j.developer_id AND j.project_id = #{id} ORDER BY t.id"
- }
- has_and_belongs_to_many :developers_by_sql, :class_name => "Developer", :delete_sql => proc { |record| "DELETE FROM developers_projects WHERE project_id = #{id} AND developer_id = #{record.id}" }
+ has_and_belongs_to_many :developers, -> { uniq.order 'developers.name desc, developers.id desc' }
+ has_and_belongs_to_many :readonly_developers, -> { readonly }, :class_name => "Developer"
+ has_and_belongs_to_many :selected_developers, -> { uniq.select "developers.*" }, :class_name => "Developer"
+ has_and_belongs_to_many :non_unique_developers, -> { order 'developers.name desc, developers.id desc' }, :class_name => 'Developer'
+ has_and_belongs_to_many :limited_developers, -> { limit 1 }, :class_name => "Developer"
+ has_and_belongs_to_many :developers_named_david, -> { where("name = 'David'").uniq }, :class_name => "Developer"
+ has_and_belongs_to_many :developers_named_david_with_hash_conditions, -> { where(:name => 'David').uniq }, :class_name => "Developer"
+ has_and_belongs_to_many :salaried_developers, -> { where "salary > 0" }, :class_name => "Developer"
has_and_belongs_to_many :developers_with_callbacks, :class_name => "Developer", :before_add => Proc.new {|o, r| o.developers_log << "before_adding#{r.id || '<new>'}"},
:after_add => Proc.new {|o, r| o.developers_log << "after_adding#{r.id || '<new>'}"},
:before_remove => Proc.new {|o, r| o.developers_log << "before_removing#{r.id}"},
:after_remove => Proc.new {|o, r| o.developers_log << "after_removing#{r.id}"}
- has_and_belongs_to_many :well_payed_salary_groups, :class_name => "Developer", :group => "developers.salary", :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary"
+ has_and_belongs_to_many :well_payed_salary_groups, -> { group("developers.salary").having("SUM(salary) > 10000").select("SUM(salary) as salary") }, :class_name => "Developer"
attr_accessor :developers_log
after_initialize :set_developers_log
diff --git a/activerecord/test/models/sponsor.rb b/activerecord/test/models/sponsor.rb
index aa4a3638fd..ec3dcf8a97 100644
--- a/activerecord/test/models/sponsor.rb
+++ b/activerecord/test/models/sponsor.rb
@@ -2,6 +2,6 @@ class Sponsor < ActiveRecord::Base
belongs_to :sponsor_club, :class_name => "Club", :foreign_key => "club_id"
belongs_to :sponsorable, :polymorphic => true
belongs_to :thing, :polymorphic => true, :foreign_type => :sponsorable_type, :foreign_key => :sponsorable_id
- belongs_to :sponsorable_with_conditions, :polymorphic => true,
- :foreign_type => 'sponsorable_type', :foreign_key => 'sponsorable_id', :conditions => {:name => 'Ernie'}
+ belongs_to :sponsorable_with_conditions, -> { where :name => 'Ernie'}, :polymorphic => true,
+ :foreign_type => 'sponsorable_type', :foreign_key => 'sponsorable_id'
end
diff --git a/activerecord/test/models/tagging.rb b/activerecord/test/models/tagging.rb
index ef323df158..f91f2ad2e9 100644
--- a/activerecord/test/models/tagging.rb
+++ b/activerecord/test/models/tagging.rb
@@ -3,12 +3,11 @@ module Taggable
end
class Tagging < ActiveRecord::Base
- belongs_to :tag, :include => :tagging
+ belongs_to :tag, -> { includes(:tagging) }
belongs_to :super_tag, :class_name => 'Tag', :foreign_key => 'super_tag_id'
belongs_to :invalid_tag, :class_name => 'Tag', :foreign_key => 'tag_id'
- belongs_to :blue_tag, :class_name => 'Tag', :foreign_key => :tag_id, :conditions => { :tags => { :name => 'Blue' } }
+ belongs_to :blue_tag, -> { where :tags => { :name => 'Blue' } }, :class_name => 'Tag', :foreign_key => :tag_id
belongs_to :tag_with_primary_key, :class_name => 'Tag', :foreign_key => :tag_id, :primary_key => :custom_primary_key
- belongs_to :interpolated_tag, :class_name => 'Tag', :foreign_key => :tag_id, :conditions => proc { "1 = #{1}" }
belongs_to :taggable, :polymorphic => true, :counter_cache => true
has_many :things, :through => :taggable
end
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index 3e6c8893e9..1b8ea12ba8 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -1,12 +1,46 @@
require 'active_support/core_ext/hash/keys'
module ActiveSupport
- # This class has dubious semantics and we only have it so that
- # people can write <tt>params[:key]</tt> instead of <tt>params['key']</tt>
- # and they get the same value for both keys.
+ # Implements a hash where keys +:foo+ and +"foo"+ are considered to be the same.
+ #
+ # rgb = ActiveSupport::HashWithIndifferentAccess.new
+ #
+ # rgb[:black] = '#000000'
+ # rgb[:black] # => '#000000'
+ # rgb['black'] # => '#000000'
+ #
+ # rgb['white'] = '#FFFFFF'
+ # rgb[:white] # => '#FFFFFF'
+ # rgb['white'] # => '#FFFFFF'
+ #
+ # Internally symbols are mapped to strings when used as keys in the entire
+ # writing interface (calling <tt>[]=</tt>, <tt>merge</tt>, etc). This
+ # mapping belongs to the public interface. For example, given
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new(:a => 1)
+ #
+ # you are guaranteed that the key is returned as a string:
+ #
+ # hash.keys # => ["a"]
+ #
+ # Technically other types of keys are accepted:
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new(:a => 1)
+ # hash[0] = 0
+ # hash # => {"a"=>1, 0=>0}
+ #
+ # but this class is intended for use cases where strings or symbols are the
+ # expected keys and it is convenient to understand both as the same. For
+ # example the +params+ hash in Ruby on Rails.
+ #
+ # Note that core extensions define <tt>Hash#with_indifferent_access</tt>:
+ #
+ # rgb = {:black => '#000000', :white => '#FFFFFF'}.with_indifferent_access
+ #
+ # which may be handy.
class HashWithIndifferentAccess < Hash
-
- # Always returns true, so that <tt>Array#extract_options!</tt> finds members of this class.
+ # Returns true so that <tt>Array#extract_options!</tt> finds members of
+ # this class.
def extractable_options?
true
end
@@ -51,25 +85,32 @@ module ActiveSupport
# Assigns a new value to the hash:
#
- # hash = HashWithIndifferentAccess.new
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
# hash[:key] = "value"
#
+ # This value can be later fetched using either +:key+ or +"key"+.
def []=(key, value)
regular_writer(convert_key(key), convert_value(value))
end
alias_method :store, :[]=
- # Updates the instantized hash with values from the second:
+ # Updates the receiver in-place merging in the hash passed as argument:
#
- # hash_1 = HashWithIndifferentAccess.new
- # hash_1[:key] = "value"
+ # hash_1 = ActiveSupport::HashWithIndifferentAccess.new
+ # hash_2[:key] = "value"
#
- # hash_2 = HashWithIndifferentAccess.new
+ # hash_2 = ActiveSupport::HashWithIndifferentAccess.new
# hash_2[:key] = "New Value!"
#
# hash_1.update(hash_2) # => {"key"=>"New Value!"}
#
+ # The argument can be either an
+ # <tt>ActiveSupport::HashWithIndifferentAccess</tt> or a regular +Hash+.
+ # In either case the merge respects the semantics of indifferent access.
+ #
+ # If the argument is a regular hash with keys +:key+ and +"key"+ only one
+ # of the values end up in the receiver, but which was is unespecified.
def update(other_hash)
if other_hash.is_a? HashWithIndifferentAccess
super(other_hash)
@@ -83,10 +124,10 @@ module ActiveSupport
# Checks the hash for a key matching the argument passed in:
#
- # hash = HashWithIndifferentAccess.new
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
# hash["key"] = "value"
- # hash.key? :key # => true
- # hash.key? "key" # => true
+ # hash.key?(:key) # => true
+ # hash.key?("key") # => true
#
def key?(key)
super(convert_key(key))
@@ -96,14 +137,24 @@ module ActiveSupport
alias_method :has_key?, :key?
alias_method :member?, :key?
- # Fetches the value for the specified key, same as doing hash[key]
+ # Same as <tt>Hash#fetch</tt> where the key passed as argument can be
+ # either a string or a symbol:
+ #
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
+ # counters[:foo] = 1
+ #
+ # counters.fetch("foo") # => 1
+ # counters.fetch(:bar, 0) # => 0
+ # counters.fetch(:bar) {|key| 0} # => 0
+ # counters.fetch(:zoo) # => KeyError: key not found: "zoo"
+ #
def fetch(key, *extras)
super(convert_key(key), *extras)
end
# Returns an array of the values at the specified indices:
#
- # hash = HashWithIndifferentAccess.new
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
# hash[:a] = "x"
# hash[:b] = "y"
# hash.values_at("a", "b") # => ["x", "y"]
@@ -119,23 +170,30 @@ module ActiveSupport
end
end
- # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash.
- # Does not overwrite the existing hash.
+ # This method has the same semantics of +update+, except it does not
+ # modify the receiver but rather returns a new hash with indifferent
+ # access with the result of the merge.
def merge(hash)
self.dup.update(hash)
end
- # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
- # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a <tt>HashWithDifferentAccess</tt>.
+ # Like +merge+ but the other way around: Merges the receiver into the
+ # argument and returns a new hash with indifferent access as result:
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
+ # hash['a'] = nil
+ # hash.reverse_merge(:a => 0, :b => 1) # => {"a"=>nil, "b"=>1}
+ #
def reverse_merge(other_hash)
- super self.class.new_from_hash_copying_default(other_hash)
+ super(self.class.new_from_hash_copying_default(other_hash))
end
+ # Same semantics as +reverse_merge+ but modifies the receiver in-place.
def reverse_merge!(other_hash)
replace(reverse_merge( other_hash ))
end
- # Removes a specified key from the hash.
+ # Removes the specified key from the hash.
def delete(key)
super(convert_key(key))
end
@@ -150,7 +208,7 @@ module ActiveSupport
def deep_symbolize_keys; to_hash.deep_symbolize_keys end
def to_options!; self end
- # Convert to a Hash with String keys.
+ # Convert to a regular hash with string keys.
def to_hash
Hash.new(default).merge!(self)
end
diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb
index 5e080df518..9cd8ac36bc 100644
--- a/activesupport/lib/active_support/tagged_logging.rb
+++ b/activesupport/lib/active_support/tagged_logging.rb
@@ -16,7 +16,7 @@ module ActiveSupport
module Formatter # :nodoc:
# This method is invoked when a log event occurs
def call(severity, timestamp, progname, msg)
- super(severity, timestamp, progname, "#{tags_text}#{msg}")
+ super(severity, timestamp, progname, "#{tags_text} #{msg}".lstrip)
end
def clear!
@@ -31,7 +31,7 @@ module ActiveSupport
def tags_text
tags = current_tags
if tags.any?
- tags.collect { |tag| "[#{tag}] " }.join
+ tags.collect { |tag| "[#{tag}]" }.join(' ')
end
end
end
diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb
index a8d69d0ec3..ef289692bc 100644
--- a/activesupport/test/multibyte_chars_test.rb
+++ b/activesupport/test/multibyte_chars_test.rb
@@ -110,7 +110,7 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
end
%w{capitalize downcase lstrip reverse rstrip swapcase upcase}.each do |method|
- class_eval(<<-EOTESTS)
+ class_eval(<<-EOTESTS, __FILE__, __LINE__ + 1)
def test_#{method}_bang_should_return_self_when_modifying_wrapped_string
chars = ' él piDió Un bUen café '
assert_equal chars.object_id, chars.send("#{method}!").object_id
diff --git a/guides/code/getting_started/app/views/comments/_comment.html.erb b/guides/code/getting_started/app/views/comments/_comment.html.erb
index 0cebe0bd96..3d2bc1590e 100644
--- a/guides/code/getting_started/app/views/comments/_comment.html.erb
+++ b/guides/code/getting_started/app/views/comments/_comment.html.erb
@@ -10,6 +10,6 @@
<p>
<%= link_to 'Destroy Comment', [comment.post, comment],
- :confirm => 'Are you sure?',
- :method => :delete %>
+ :method => :delete,
+ :data => { :confirm => 'Are you sure?' } %>
</p>
diff --git a/guides/code/getting_started/app/views/posts/index.html.erb b/guides/code/getting_started/app/views/posts/index.html.erb
index 7b72720d50..9a0e90eadc 100644
--- a/guides/code/getting_started/app/views/posts/index.html.erb
+++ b/guides/code/getting_started/app/views/posts/index.html.erb
@@ -17,7 +17,7 @@
<td><%= post.text %></td>
<td><%= link_to 'Show', :action => :show, :id => post.id %>
<td><%= link_to 'Edit', :action => :edit, :id => post.id %>
- <td><%= link_to 'Destroy', { :action => :destroy, :id => post.id }, :method => :delete, :confirm => 'Are you sure?' %>
+ <td><%= link_to 'Destroy', { :action => :destroy, :id => post.id }, :method => :delete, :data => { :confirm => 'Are you sure?' } %>
</tr>
<% end %>
</table>
diff --git a/guides/source/active_support_core_extensions.textile b/guides/source/active_support_core_extensions.textile
index 99b8bdd3fd..66de6fd310 100644
--- a/guides/source/active_support_core_extensions.textile
+++ b/guides/source/active_support_core_extensions.textile
@@ -2783,27 +2783,7 @@ The method +assert_valid_keys+ receives an arbitrary number of arguments, and ch
{:a => 1}.assert_valid_keys("a") # ArgumentError
</ruby>
-Active Record does not accept unknown options when building associations for example. It implements that control via +assert_valid_keys+:
-
-<ruby>
-mattr_accessor :valid_keys_for_has_many_association
-@@valid_keys_for_has_many_association = [
- :class_name, :table_name, :foreign_key, :primary_key,
- :dependent,
- :select, :conditions, :include, :order, :group, :having, :limit, :offset,
- :as, :through, :source, :source_type,
- :uniq,
- :finder_sql, :counter_sql,
- :before_add, :after_add, :before_remove, :after_remove,
- :extend, :readonly,
- :validate, :inverse_of
-]
-
-def create_has_many_reflection(association_id, options, &extension)
- options.assert_valid_keys(valid_keys_for_has_many_association)
- ...
-end
-</ruby>
+Active Record does not accept unknown options when building associations, for example. It implements that control via +assert_valid_keys+.
NOTE: Defined in +active_support/core_ext/hash/keys.rb+.
diff --git a/guides/source/ajax_on_rails.textile b/guides/source/ajax_on_rails.textile
index e23fdf9a74..26e0270a31 100644
--- a/guides/source/ajax_on_rails.textile
+++ b/guides/source/ajax_on_rails.textile
@@ -129,7 +129,7 @@ will produce
<ruby>
button_to 'Delete Image', { action: 'delete', id: @image.id },
- confirm: 'Are you sure?', method: :delete
+ method: :delete, data: { confirm: 'Are you sure?' }
</ruby>
will produce
@@ -144,8 +144,8 @@ will produce
</html>
<ruby>
-button_to 'Destroy', 'http://www.example.com', confirm: 'Are you sure?',
- method: 'delete', remote: true, data: { disable_with: 'loading...' }
+button_to 'Destroy', 'http://www.example.com',
+ method: 'delete', remote: true, data: { disable_with: 'loading...', confirm: 'Are you sure?' }
</ruby>
will produce
@@ -217,7 +217,6 @@ link_to_remote "Delete the item",
Note that if we wouldn't override the default behavior (POST), the above snippet would route to the create action rather than destroy.
** *JavaScript filters* You can customize the remote call further by wrapping it with some JavaScript code. Let's say in the previous example, when deleting a link, you'd like to ask for a confirmation by showing a simple modal text box to the user. This is a typical example what you can accomplish with these options - let's see them one by one:
-*** +:confirm+ =&gt; +msg+ Pops up a JavaScript confirmation dialog, displaying +msg+. If the user chooses 'OK', the request is launched, otherwise canceled.
*** +:condition+ =&gt; +code+ Evaluates +code+ (which should evaluate to a boolean) and proceeds if it's true, cancels the request otherwise.
*** +:before+ =&gt; +code+ Evaluates the +code+ just before launching the request. The output of the code has no influence on the execution. Typically used show a progress indicator (see this in action in the next example).
*** +:after+ =&gt; +code+ Evaluates the +code+ after launching the request. Note that this is different from the +:success+ or +:complete+ callback (covered in the next section) since those are triggered after the request is completed, while the code snippet passed to +:after+ is evaluated after the remote call is made. A common example is to disable elements on the page or otherwise prevent further action while the request is completed.
@@ -307,4 +306,4 @@ JavaScript testing reminds me the definition of the world 'classic' by Mark Twai
* Cucumber+Webrat
* Mention stuff like screw.unit/jsSpec
-Note to self: check out the RailsConf JS testing video \ No newline at end of file
+Note to self: check out the RailsConf JS testing video
diff --git a/guides/source/association_basics.textile b/guides/source/association_basics.textile
index 8ddc56bef1..4dca7a508c 100644
--- a/guides/source/association_basics.textile
+++ b/guides/source/association_basics.textile
@@ -1257,10 +1257,8 @@ The +has_many+ association supports these options:
* +:autosave+
* +:class_name+
* +:conditions+
-* +:counter_sql+
* +:dependent+
* +:extend+
-* +:finder_sql+
* +:foreign_key+
* +:group+
* +:include+
@@ -1326,12 +1324,6 @@ class Customer < ActiveRecord::Base
end
</ruby>
-h6(#has_many-counter_sql). +:counter_sql+
-
-Normally Rails automatically generates the proper SQL to count the association members. With the +:counter_sql+ option, you can specify a complete SQL statement to count them yourself.
-
-NOTE: If you specify +:finder_sql+ but not +:counter_sql+, then the counter SQL will be generated by substituting the +SELECT ... FROM+ clause of your +:finder_sql+ statement by +SELECT COUNT(*) FROM+.
-
h6(#has_many-dependent). +:dependent+
If you set the +:dependent+ option to +:destroy+, then deleting this object will call the +destroy+ method on the associated objects to delete those objects. If you set the +:dependent+ option to +:delete_all+, then deleting this object will delete the associated objects _without_ calling their +destroy+ method. If you set the +:dependent+ option to +:nullify+, then deleting this object will set the foreign key in the associated objects to +NULL+.
@@ -1345,10 +1337,6 @@ h6(#has_many-extend). +:extend+
The +:extend+ option specifies a named module to extend the association proxy. Association extensions are discussed in detail <a href="#association-extensions">later in this guide</a>.
-h6(#has_many-finder_sql). +:finder_sql+
-
-Normally Rails automatically generates the proper SQL to fetch the association members. With the +:finder_sql+ option, you can specify a complete SQL statement to fetch them yourself. If fetching objects requires complex multi-table SQL, this may be necessary.
-
h6(#has_many-foreign_key). +:foreign_key+
By convention, Rails assumes that the column used to hold the foreign key on the other model is the name of this model with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly:
@@ -1700,14 +1688,10 @@ The +has_and_belongs_to_many+ association supports these options:
* +:autosave+
* +:class_name+
* +:conditions+
-* +:counter_sql+
-* +:delete_sql+
* +:extend+
-* +:finder_sql+
* +:foreign_key+
* +:group+
* +:include+
-* +:insert_sql+
* +:join_table+
* +:limit+
* +:offset+
@@ -1767,24 +1751,10 @@ end
If you use a hash-style +:conditions+ option, then record creation via this association will be automatically scoped using the hash. In this case, using +@parts.assemblies.create+ or +@parts.assemblies.build+ will create orders where the +factory+ column has the value "Seattle".
-h6(#has_and_belongs_to_many-counter_sql). +:counter_sql+
-
-Normally Rails automatically generates the proper SQL to count the association members. With the +:counter_sql+ option, you can specify a complete SQL statement to count them yourself.
-
-NOTE: If you specify +:finder_sql+ but not +:counter_sql+, then the counter SQL will be generated by substituting the +SELECT ... FROM+ clause of your +:finder_sql+ statement by +SELECT COUNT(*) FROM+.
-
-h6(#has_and_belongs_to_many-delete_sql). +:delete_sql+
-
-Normally Rails automatically generates the proper SQL to remove links between the associated classes. With the +:delete_sql+ option, you can specify a complete SQL statement to delete them yourself.
-
h6(#has_and_belongs_to_many-extend). +:extend+
The +:extend+ option specifies a named module to extend the association proxy. Association extensions are discussed in detail <a href="#association-extensions">later in this guide</a>.
-h6(#has_and_belongs_to_many-finder_sql). +:finder_sql+
-
-Normally Rails automatically generates the proper SQL to fetch the association members. With the +:finder_sql+ option, you can specify a complete SQL statement to fetch them yourself. If fetching objects requires complex multi-table SQL, this may be necessary.
-
h6(#has_and_belongs_to_many-foreign_key). +:foreign_key+
By convention, Rails assumes that the column in the join table used to hold the foreign key pointing to this model is the name of this model with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly:
@@ -1811,10 +1781,6 @@ h6(#has_and_belongs_to_many-include). +:include+
You can use the +:include+ option to specify second-order associations that should be eager-loaded when this association is used.
-h6(#has_and_belongs_to_many-insert_sql). +:insert_sql+
-
-Normally Rails automatically generates the proper SQL to create links between the associated classes. With the +:insert_sql+ option, you can specify a complete SQL statement to insert them yourself.
-
h6(#has_and_belongs_to_many-join_table). +:join_table+
If the default name of the join table, based on lexical ordering, is not what you want, you can use the +:join_table+ option to override the default.
diff --git a/guides/source/getting_started.textile b/guides/source/getting_started.textile
index b00d9af00c..8d7c0d4bea 100644
--- a/guides/source/getting_started.textile
+++ b/guides/source/getting_started.textile
@@ -1144,7 +1144,7 @@ together.
<td><%= post.text %></td>
<td><%= link_to 'Show', :action => :show, :id => post.id %></td>
<td><%= link_to 'Edit', :action => :edit, :id => post.id %></td>
- <td><%= link_to 'Destroy', { :action => :destroy, :id => post.id }, :method => :delete, :confirm => 'Are you sure?' %></td>
+ <td><%= link_to 'Destroy', { :action => :destroy, :id => post.id }, :method => :delete, :data => { :confirm => 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
@@ -1152,13 +1152,12 @@ together.
Here we're using +link_to+ in a different way. We wrap the
+:action+ and +:id+ attributes in a hash so that we can pass those two keys in
-first as one argument, and then the final two keys as another argument. The +:method+ and +:confirm+
+first as one argument, and then the final two keys as another argument. The +:method+ and +:'data-confirm'+
options are used as HTML5 attributes so that when the link is clicked,
-Rails will first show a confirm dialog to the user, and then submit the
-link with method +delete+. This is done via the JavaScript file +jquery_ujs+
-which is automatically included into your application's layout
-(+app/views/layouts/application.html.erb+) when you generated the application.
-Without this file, the confirmation dialog box wouldn't appear.
+Rails will first show a confirm dialog to the user, and then submit the link with method +delete+.
+This is done via the JavaScript file +jquery_ujs+ which is automatically included
+into your application's layout (+app/views/layouts/application.html.erb+) when you
+generated the application. Without this file, the confirmation dialog box wouldn't appear.
!images/getting_started/confirm_dialog.png(Confirm Dialog)!
@@ -1627,8 +1626,8 @@ So first, let's add the delete link in the
<p>
<%= link_to 'Destroy Comment', [comment.post, comment],
- :confirm => 'Are you sure?',
- :method => :delete %>
+ :method => :delete,
+ :data => { :confirm => 'Are you sure?' } %>
</p>
</erb>
diff --git a/guides/source/layouts_and_rendering.textile b/guides/source/layouts_and_rendering.textile
index 55bd521419..32ceecea18 100644
--- a/guides/source/layouts_and_rendering.textile
+++ b/guides/source/layouts_and_rendering.textile
@@ -78,7 +78,7 @@ If we want to display the properties of all the books in our view, we can do so
<td><%= book.content %></td>
<td><%= link_to "Show", book %></td>
<td><%= link_to "Edit", edit_book_path(book) %></td>
- <td><%= link_to "Remove", book, :confirm => "Are you sure?", :method => :delete %></td>
+ <td><%= link_to "Remove", book, :method => :delete, :data => { :confirm => "Are you sure?" } %></td>
</tr>
<% end %>
</table>
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 3afbf0a03e..da5b33c0fb 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -177,7 +177,7 @@ module Rails
end
def queue #:nodoc:
- @queue ||= build_queue
+ @queue ||= Queueing::Container.new(build_queue)
end
def build_queue #:nodoc:
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
index d78d97b2b4..f5182bcc50 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
+++ b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
@@ -19,7 +19,7 @@
<% end -%>
<td><%%= link_to 'Show', <%= singular_table_name %> %></td>
<td><%%= link_to 'Edit', edit_<%= singular_table_name %>_path(<%= singular_table_name %>) %></td>
- <td><%%= link_to 'Destroy', <%= singular_table_name %>, confirm: 'Are you sure?', method: :delete %></td>
+ <td><%%= link_to 'Destroy', <%= singular_table_name %>, method: :delete, data: { confirm: 'Are you sure?' } %></td>
<%% end %>
</tbody>
</table>
diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb
index d480fc12b5..d2c2abf40c 100644
--- a/railties/lib/rails/generators/generated_attribute.rb
+++ b/railties/lib/rails/generators/generated_attribute.rb
@@ -8,6 +8,7 @@ module Rails
attr_accessor :name, :type
attr_reader :attr_options
+ attr_writer :index_name
class << self
def parse(column_definition)
@@ -89,18 +90,26 @@ module Rails
end
end
+ def plural_name
+ name.sub(/_id$/, '').pluralize
+ end
+
def human_name
- name.to_s.humanize
+ name.humanize
end
def index_name
- if reference?
+ @index_name ||= if reference?
polymorphic? ? %w(id type).map { |t| "#{name}_#{t}" } : "#{name}_id"
else
name
end
end
+ def foreign_key?
+ !!(name =~ /_id$/)
+ end
+
def reference?
self.class.reference?(type)
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
index 01f9396403..fee9eb9456 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
@@ -35,7 +35,7 @@
# Do not compress assets.
config.assets.compress = false
- # Expands the lines which load the assets.
+ # Debug mode disables concatenation and preprocessing of assets.
config.assets.debug = true
<%- end -%>
diff --git a/railties/lib/rails/queueing.rb b/railties/lib/rails/queueing.rb
index 8a76914548..892cf8e894 100644
--- a/railties/lib/rails/queueing.rb
+++ b/railties/lib/rails/queueing.rb
@@ -1,7 +1,34 @@
require "thread"
+require 'delegate'
module Rails
module Queueing
+ # A container for multiple queues. This class delegates to a default Queue
+ # so that Rails.queue.push and friends will Just Work. To use this class
+ # with multiple queues:
+ #
+ # # In your configuration:
+ # Rails.queue[:image_queue] = SomeQueue.new
+ # Rails.queue[:mail_queue] = SomeQueue.new
+ #
+ # # In your app code:
+ # Rails.queue[:mail_queue].push SomeJob.new
+ #
+ class Container < DelegateClass(::Queue)
+ def initialize(default_queue)
+ @queues = { :default => default_queue }
+ super(default_queue)
+ end
+
+ def [](queue_name)
+ @queues[queue_name]
+ end
+
+ def []=(queue_name, queue)
+ @queues[queue_name] = queue
+ end
+ end
+
# A Queue that simply inherits from STDLIB's Queue. Everytime this
# queue is used, Rails automatically sets up a ThreadedConsumer
# to consume it.
diff --git a/railties/test/application/queue_test.rb b/railties/test/application/queue_test.rb
index 22d6c20404..83ca981336 100644
--- a/railties/test/application/queue_test.rb
+++ b/railties/test/application/queue_test.rb
@@ -20,14 +20,14 @@ module ApplicationTests
test "the queue is a TestQueue in test mode" do
app("test")
- assert_kind_of Rails::Queueing::TestQueue, Rails.application.queue
- assert_kind_of Rails::Queueing::TestQueue, Rails.queue
+ assert_kind_of Rails::Queueing::TestQueue, Rails.application.queue[:default]
+ assert_kind_of Rails::Queueing::TestQueue, Rails.queue[:default]
end
test "the queue is a Queue in development mode" do
app("development")
- assert_kind_of Rails::Queueing::Queue, Rails.application.queue
- assert_kind_of Rails::Queueing::Queue, Rails.queue
+ assert_kind_of Rails::Queueing::Queue, Rails.application.queue[:default]
+ assert_kind_of Rails::Queueing::Queue, Rails.queue[:default]
end
class ThreadTrackingJob
@@ -144,7 +144,7 @@ module ApplicationTests
test "a custom queue implementation can be provided" do
setup_custom_queue
- assert_kind_of MyQueue, Rails.queue
+ assert_kind_of MyQueue, Rails.queue[:default]
job = Struct.new(:id, :ran) do
def run
diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb
index 86e3793289..774038c0e1 100644
--- a/railties/test/generators/migration_generator_test.rb
+++ b/railties/test/generators/migration_generator_test.rb
@@ -167,6 +167,19 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_create_join_table_migration
+ migration = "add_media_join_table"
+ run_generator [migration, "artist_id", "musics:uniq"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |up|
+ assert_match(/create_join_table :artists, :musics/, up)
+ assert_match(/# t.index \[:artist_id, :music_id\]/, up)
+ assert_match(/ t.index \[:music_id, :artist_id\], unique: true/, up)
+ end
+ end
+ end
+
def test_should_create_empty_migrations_if_name_not_start_with_add_or_remove
migration = "create_books"
run_generator [migration, "title:string", "content:text"]
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index ad2f85a5ff..027d8eb9b7 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -167,7 +167,7 @@ class GeneratorsTest < Rails::Generators::TestCase
def test_developer_options_are_overwriten_by_user_options
Rails::Generators.options[:with_options] = { :generate => false }
- self.class.class_eval <<-end_eval
+ self.class.class_eval(<<-end_eval, __FILE__, __LINE__ + 1)
class WithOptionsGenerator < Rails::Generators::Base
class_option :generate, :default => true
end
diff --git a/railties/test/queueing/container_test.rb b/railties/test/queueing/container_test.rb
new file mode 100644
index 0000000000..69e59a3871
--- /dev/null
+++ b/railties/test/queueing/container_test.rb
@@ -0,0 +1,30 @@
+require 'abstract_unit'
+require 'rails/queueing'
+
+module Rails
+ module Queueing
+ class ContainerTest < ActiveSupport::TestCase
+ def test_delegates_to_default
+ q = Queue.new
+ container = Container.new q
+ job = Object.new
+
+ container.push job
+ assert_equal job, q.pop
+ end
+
+ def test_access_default
+ q = Queue.new
+ container = Container.new q
+ assert_equal q, container[:default]
+ end
+
+ def test_assign_queue
+ container = Container.new Object.new
+ q = Object.new
+ container[:foo] = q
+ assert_equal q, container[:foo]
+ end
+ end
+ end
+end