提交 4e69a157 编写于 作者: O Oscar Del Ben

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

......@@ -22,12 +22,12 @@ the email.
This can be as simple as:
class Notifier < ActionMailer::Base
delivers_from 'system@loudthinking.com'
default from: 'system@loudthinking.com'
def welcome(recipient)
@recipient = recipient
mail(:to => recipient,
:subject => "[Signed up] Welcome #{recipient}")
mail(to: recipient,
subject: "[Signed up] Welcome #{recipient}")
end
end
......
......@@ -16,7 +16,7 @@ module DataStreaming
protected
# Sends the file. This uses a server-appropriate method (such as X-Sendfile)
# via the Rack::Sendfile middleware. The header to use is set via
# config.action_dispatch.x_sendfile_header.
# +config.action_dispatch.x_sendfile_header+.
# Your server can also configure this for you by setting the X-Sendfile-Type header.
#
# Be careful to sanitize the path parameter if it is coming from a web
......
......@@ -48,7 +48,7 @@ module ActionController
# method attribute_names.
#
# If you're going to pass the parameters to an +ActiveModel+ object (such as
# +User.new(params[:user])+), you might consider passing the model class to
# <tt>User.new(params[:user])</tt>), you might consider passing the model class to
# the method instead. The +ParamsWrapper+ will actually try to determine the
# list of attribute names from the model and only wrap those attributes:
#
......
......@@ -27,13 +27,13 @@ def setup_subscriptions
path = payload[:virtual_path]
next unless path
partial = path =~ /^.*\/_[^\/]*$/
if partial
@partials[path] += 1
@partials[path.split("/").last] += 1
@templates[path] += 1
else
@templates[path] += 1
end
@templates[path] += 1
end
end
......
......@@ -225,7 +225,7 @@ def permanent
# cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception will
# be raised.
#
# This jar requires that you set a suitable secret for the verification on your app's config.secret_token.
# This jar requires that you set a suitable secret for the verification on your app's +config.secret_token+.
#
# Example:
#
......
......@@ -18,10 +18,10 @@ module ActionDispatch
# classes before they are unloaded.
#
# By default, ActionDispatch::Reloader is included in the middleware stack
# only in the development environment; specifically, when config.cache_classes
# only in the development environment; specifically, when +config.cache_classes+
# is false. Callbacks may be registered even when it is not included in the
# middleware stack, but are executed only when +ActionDispatch::Reloader.prepare!+
# or +ActionDispatch::Reloader.cleanup!+ are called manually.
# middleware stack, but are executed only when <tt>ActionDispatch::Reloader.prepare!</tt>
# or <tt>ActionDispatch::Reloader.cleanup!</tt> are called manually.
#
class Reloader
include ActiveSupport::Callbacks
......
......@@ -1305,6 +1305,21 @@ def shallow?
parent_resource.instance_of?(Resource) && @scope[:shallow]
end
def draw(name)
path = @draw_paths.find do |_path|
_path.join("#{name}.rb").file?
end
unless path
msg = "Your router tried to #draw the external file #{name}.rb,\n" \
"but the file was not found in:\n\n"
msg += @draw_paths.map { |_path| " * #{_path}" }.join("\n")
raise msg
end
instance_eval(path.join("#{name}.rb").read)
end
# match 'path' => 'controller#action'
# match 'path', to: 'controller#action'
# match 'path', 'otherpath', on: :member, via: :get
......@@ -1554,6 +1569,7 @@ def name_for_action(as, action) #:nodoc:
def initialize(set) #:nodoc:
@set = set
@draw_paths = set.draw_paths
@scope = { :path_names => @set.resources_path_names }
end
......
......@@ -255,6 +255,7 @@ def optimized_helper(route)
attr_accessor :formatter, :set, :named_routes, :default_scope, :router
attr_accessor :disable_clear_and_finalize, :resources_path_names
attr_accessor :default_url_options, :request_class, :valid_conditions
attr_accessor :draw_paths
alias :routes :set
......@@ -266,6 +267,7 @@ def initialize(request_class = ActionDispatch::Request)
self.named_routes = NamedRouteCollection.new
self.resources_path_names = self.class.default_resources_path_names.dup
self.default_url_options = {}
self.draw_paths = []
self.request_class = request_class
@valid_conditions = {}
......
......@@ -113,7 +113,7 @@ def stylesheet_url(source)
# == Caching multiple stylesheets into one
#
# You can also cache multiple stylesheets into one file, which requires less HTTP connections and can better be
# compressed by gzip (leading to faster transfers). Caching will only happen if config.perform_caching
# compressed by gzip (leading to faster transfers). Caching will only happen if +config.perform_caching+
# is set to true (which is the case by default for the Rails production environment, but not for the development
# environment). Examples:
#
......
......@@ -233,7 +233,7 @@ def number_with_delimiter(number, options = {})
parts = number.to_s.to_str.split('.')
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
parts.join(options[:separator]).html_safe
safe_join(parts, options[:separator])
end
# Formats a +number+ with the specified level of <tt>:precision</tt> (e.g., 112.32 has a precision
......@@ -392,10 +392,10 @@ def number_to_human_size(number, options = {})
# * *integers*: <tt>:unit</tt>, <tt>:ten</tt>, <tt>:hundred</tt>, <tt>:thousand</tt>, <tt>:million</tt>, <tt>:billion</tt>, <tt>:trillion</tt>, <tt>:quadrillion</tt>
# * *fractionals*: <tt>:deci</tt>, <tt>:centi</tt>, <tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>, <tt>:pico</tt>, <tt>:femto</tt>
# * <tt>:format</tt> - Sets the format of the output string (defaults to "%n %u"). The field types are:
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
#
# %u The quantifier (ex.: 'thousand')
# %n The number
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
#
#
# ==== Examples
# number_to_human(123) # => "123"
......
......@@ -94,10 +94,10 @@ def content_tag_for(tag_name, single_or_multiple_records, prefix = nil, options
# for each record.
def content_tag_for_single_record(tag_name, record, prefix, options, &block)
options = options ? options.dup : {}
options.merge!(:class => "#{dom_class(record, prefix)} #{options[:class]}".rstrip, :id => dom_id(record, prefix))
options[:class] = "#{dom_class(record, prefix)} #{options[:class]}".rstrip
options[:id] = dom_id(record, prefix)
content = block.arity == 0 ? capture(&block) : capture(record, &block)
content_tag(tag_name, content, options)
content_tag(tag_name, capture(record, &block), options)
end
end
end
......
......@@ -16,7 +16,7 @@ def render_with_scope
end
def render_with_has_many_through_association
@developer = Developer.find(:first)
@developer = Developer.first
render :partial => @developer.topics
end
......@@ -31,7 +31,7 @@ def render_with_belongs_to_association
end
def render_with_record
@developer = Developer.find(:first)
@developer = Developer.first
render :partial => @developer
end
......
......@@ -4,11 +4,12 @@ module ActionDispatch
module Routing
class MapperTest < ActiveSupport::TestCase
class FakeSet
attr_reader :routes
attr_reader :routes, :draw_paths
alias :set :routes
def initialize
@routes = []
@draw_paths = []
end
def resources_path_names
......
......@@ -5,6 +5,7 @@
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/enumerable'
require 'active_support/deprecation'
require 'active_support/core_ext/object/try'
require 'active_support/descendants_tracker'
module ActiveModel
......@@ -212,11 +213,7 @@ def observed_classes
# The class observed by default is inferred from the observer's class name:
# assert_equal Person, PersonObserver.observed_class
def observed_class
if observed_class_name = name[/(.*)Observer/, 1]
observed_class_name.constantize
else
nil
end
name[/(.*)Observer/, 1].try :constantize
end
end
......
......@@ -132,7 +132,8 @@ def target_scope
# ActiveRecord::RecordNotFound is rescued within the method, and it is
# not reraised. The proxy is \reset and +nil+ is the return value.
def load_target
@target ||= find_target if find_target?
@target = find_target if (@stale_state && stale_target?) || find_target?
loaded! unless loaded?
target
rescue ActiveRecord::RecordNotFound
......
......@@ -77,7 +77,7 @@ def target_id
end
def stale_state
owner[reflection.foreign_key].to_s
owner[reflection.foreign_key] && owner[reflection.foreign_key].to_s
end
end
end
......
......@@ -27,7 +27,8 @@ def raise_on_type_mismatch(record)
end
def stale_state
[super, owner[reflection.foreign_type].to_s]
foreign_key = super
foreign_key && [foreign_key.to_s, owner[reflection.foreign_type].to_s]
end
end
end
......
......@@ -61,11 +61,15 @@ def proxy_association
@association
end
def scoped
def scoped(options = nil)
association = @association
association.scoped.extending do
scope = association.scoped
scope.extending! do
define_method(:proxy_association) { association }
end
scope.merge!(options) if options
scope
end
def respond_to?(name, include_private = false)
......@@ -126,6 +130,19 @@ def reload
proxy_association.reload
self
end
# Define array public methods because we know it should be invoked over
# the target, so we can have a performance improvement using those methods
# in association collections
Array.public_instance_methods.each do |m|
unless method_defined?(m)
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{m}(*args, &block)
target.public_send(:#{m}, *args, &block) if load_target
end
RUBY
end
end
end
end
end
......@@ -62,7 +62,7 @@ def construct_join_attributes(*records)
# properly support stale-checking for nested associations.
def stale_state
if through_reflection.macro == :belongs_to
owner[through_reflection.foreign_key].to_s
owner[through_reflection.foreign_key] && owner[through_reflection.foreign_key].to_s
end
end
......
......@@ -59,7 +59,7 @@ def exec_query(sql, name = 'SQL', binds = [])
# Executes insert +sql+ statement in the context of this connection using
# +binds+ as the bind substitutes. +name+ is logged along with
# the executed +sql+ statement.
def exec_insert(sql, name, binds)
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
exec_query(sql, name, binds)
end
......@@ -87,7 +87,7 @@ def exec_update(sql, name, binds)
# passed in as +id_value+.
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
value = exec_insert(sql, name, binds)
value = exec_insert(sql, name, binds, pk, sequence_name)
id_value || last_inserted_id(value)
end
......
......@@ -226,7 +226,7 @@ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
end
alias :create :insert_sql
def exec_insert(sql, name, binds)
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
execute to_sql(sql, binds), name
end
......
......@@ -17,7 +17,7 @@ def postgresql_connection(config) # :nodoc:
# Forward any unused config params to PGconn.connect.
[:statement_limit, :encoding, :min_messages, :schema_search_path,
:schema_order, :adapter, :pool, :wait_timeout, :template,
:reaping_frequency].each do |key|
:reaping_frequency, :insert_returning].each do |key|
conn_params.delete key
end
conn_params.delete_if { |k,v| v.nil? }
......@@ -88,9 +88,8 @@ def string_to_hstore(string)
def escape_hstore(value)
value.nil? ? 'NULL'
: value =~ /[=\s,>]/ ? '"%s"' % value.gsub(/(["\\])/, '\\\\\1')
: value == "" ? '""'
: value.to_s.gsub(/(["\\])/, '\\\\\1')
: '"%s"' % value.to_s.gsub(/(["\\])/, '\\\\\1')
end
end
# :startdoc:
......@@ -259,6 +258,8 @@ def simplified_type(field_type)
# <encoding></tt> call on the connection.
# * <tt>:min_messages</tt> - An optional client min messages that is used in a
# <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
# * <tt>:insert_returning</tt> - An optional boolean to control the use or <tt>RETURNING</tt> for <tt>INSERT<tt> statements
# defaults to true.
#
# Any further options are used as connection parameters to libpq. See
# http://www.postgresql.org/docs/9.1/static/libpq-connect.html for the
......@@ -406,6 +407,7 @@ def initialize(connection, logger, connection_parameters, config)
initialize_type_map
@local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
@use_insert_returning = @config.key?(:insert_returning) ? @config[:insert_returning] : true
end
# Clears the prepared statements cache.
......@@ -667,8 +669,11 @@ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
pk = primary_key(table_ref) if table_ref
end
if pk
if pk && use_insert_returning?
select_value("#{sql} RETURNING #{quote_column_name(pk)}")
elsif pk
super
last_insert_id_value(sequence_name || default_sequence_name(table_ref, pk))
else
super
end
......@@ -783,11 +788,27 @@ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
pk = primary_key(table_ref) if table_ref
end
sql = "#{sql} RETURNING #{quote_column_name(pk)}" if pk
if pk && use_insert_returning?
sql = "#{sql} RETURNING #{quote_column_name(pk)}"
end
[sql, binds]
end
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
val = exec_query(sql, name, binds)
if !use_insert_returning? && pk
unless sequence_name
table_ref = extract_table_ref_from_insert_sql(sql)
sequence_name = default_sequence_name(table_ref, pk)
return val unless sequence_name
end
last_insert_id_result(sequence_name)
else
val
end
end
# Executes an UPDATE query and returns the number of affected tuples.
def update_sql(sql, name = nil)
super.cmd_tuples
......@@ -1028,7 +1049,9 @@ def client_min_messages=(level)
# Returns the sequence name for a table's primary key or some other specified key.
def default_sequence_name(table_name, pk = nil) #:nodoc:
serial_sequence(table_name, pk || 'id').split('.').last
result = serial_sequence(table_name, pk || 'id')
return nil unless result
result.split('.').last
rescue ActiveRecord::StatementInvalid
"#{table_name}_#{pk || 'id'}_seq"
end
......@@ -1236,6 +1259,10 @@ def extract_schema_and_table(name)
end
end
def use_insert_returning?
@use_insert_returning
end
protected
# Returns the version of the connected PostgreSQL server.
def postgresql_version
......@@ -1365,8 +1392,15 @@ def configure_connection
# Returns the current ID of a table's sequence.
def last_insert_id(sequence_name) #:nodoc:
r = exec_query("SELECT currval($1)", 'SQL', [[nil, sequence_name]])
Integer(r.rows.first.first)
Integer(last_insert_id_value(sequence_name))
end
def last_insert_id_value(sequence_name)
last_insert_id_result(sequence_name).rows.first.first
end
def last_insert_id_result(sequence_name) #:nodoc:
exec_query("SELECT currval($1)", 'SQL', [[nil, sequence_name]])
end
# Executes a SELECT query and returns the results, performing any data type
......
require 'active_record/connection_adapters/sqlite_adapter'
require 'active_record/connection_adapters/abstract_adapter'
require 'active_record/connection_adapters/statement_pool'
require 'arel/visitors/bind_visitor'
gem 'sqlite3', '~> 1.3.5'
gem 'sqlite3', '~> 1.3.6'
require 'sqlite3'
module ActiveRecord
......@@ -35,7 +37,184 @@ def sqlite3_connection(config) # :nodoc:
end
module ConnectionAdapters #:nodoc:
class SQLite3Adapter < SQLiteAdapter # :nodoc:
class SQLite3Column < Column #:nodoc:
class << self
def binary_to_string(value)
if value.encoding != Encoding::ASCII_8BIT
value = value.force_encoding(Encoding::ASCII_8BIT)
end
value
end
end
end
# The SQLite3 adapter works SQLite 3.6.16 or newer
# with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
#
# Options:
#
# * <tt>:database</tt> - Path to the database file.
class SQLite3Adapter < AbstractAdapter
class Version
include Comparable
def initialize(version_string)
@version = version_string.split('.').map { |v| v.to_i }
end
def <=>(version_string)
@version <=> version_string.split('.').map { |v| v.to_i }
end
end
class StatementPool < ConnectionAdapters::StatementPool
def initialize(connection, max)
super
@cache = Hash.new { |h,pid| h[pid] = {} }
end
def each(&block); cache.each(&block); end
def key?(key); cache.key?(key); end
def [](key); cache[key]; end
def length; cache.length; end
def []=(sql, key)
while @max <= cache.size
dealloc(cache.shift.last[:stmt])
end
cache[sql] = key
end
def clear
cache.values.each do |hash|
dealloc hash[:stmt]
end
cache.clear
end
private
def cache
@cache[$$]
end
def dealloc(stmt)
stmt.close unless stmt.closed?
end
end
class BindSubstitution < Arel::Visitors::SQLite # :nodoc:
include Arel::Visitors::BindVisitor
end
def initialize(connection, logger, config)
super(connection, logger)
@statements = StatementPool.new(@connection,
config.fetch(:statement_limit) { 1000 })
@config = config
if config.fetch(:prepared_statements) { true }
@visitor = Arel::Visitors::SQLite.new self
else
@visitor = BindSubstitution.new self
end
end
def adapter_name #:nodoc:
'SQLite'
end
# Returns true
def supports_ddl_transactions?
true
end
# Returns true if SQLite version is '3.6.8' or greater, false otherwise.
def supports_savepoints?
sqlite_version >= '3.6.8'
end
# Returns true, since this connection adapter supports prepared statement
# caching.
def supports_statement_cache?
true
end
# Returns true, since this connection adapter supports migrations.
def supports_migrations? #:nodoc:
true
end
# Returns true.
def supports_primary_key? #:nodoc:
true
end
def requires_reloading?
true
end
# Returns true
def supports_add_column?
true
end
# Disconnects from the database if already connected. Otherwise, this
# method does nothing.
def disconnect!
super
clear_cache!
@connection.close rescue nil
end
# Clears the prepared statements cache.
def clear_cache!
@statements.clear
end
# Returns true
def supports_count_distinct? #:nodoc:
true
end
# Returns true
def supports_autoincrement? #:nodoc:
true
end
def supports_index_sort_order?
true
end
def native_database_types #:nodoc:
{
:primary_key => default_primary_key_type,
:string => { :name => "varchar", :limit => 255 },
:text => { :name => "text" },
:integer => { :name => "integer" },
:float => { :name => "float" },
:decimal => { :name => "decimal" },
:datetime => { :name => "datetime" },
:timestamp => { :name => "datetime" },
:time => { :name => "time" },
:date => { :name => "date" },
:binary => { :name => "blob" },
:boolean => { :name => "boolean" }
}
end
# Returns the current database encoding format as a string, eg: 'UTF-8'
def encoding
@connection.encoding.to_s
end
# Returns true.
def supports_explain?
true
end
# QUOTING ==================================================
def quote(value, column = nil)
if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
s = column.class.string_to_binary(value).unpack("H*")[0]
......@@ -45,10 +224,387 @@ def quote(value, column = nil)
end
end
# Returns the current database encoding format as a string, eg: 'UTF-8'
def encoding
@connection.encoding.to_s
def quote_string(s) #:nodoc:
@connection.class.quote(s)
end
def quote_column_name(name) #:nodoc:
%Q("#{name.to_s.gsub('"', '""')}")
end
# Quote date/time values for use in SQL input. Includes microseconds
# if the value is a Time responding to usec.
def quoted_date(value) #:nodoc:
if value.respond_to?(:usec)
"#{super}.#{sprintf("%06d", value.usec)}"
else
super
end
end
def type_cast(value, column) # :nodoc:
return value.to_f if BigDecimal === value
return super unless String === value
return super unless column && value
value = super
if column.type == :string && value.encoding == Encoding::ASCII_8BIT
logger.error "Binary data inserted for `string` type on column `#{column.name}`" if logger
value.encode! 'utf-8'
end
value
end
# DATABASE STATEMENTS ======================================
def explain(arel, binds = [])
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
end
class ExplainPrettyPrinter
# Pretty prints the result of a EXPLAIN QUERY PLAN in a way that resembles
# the output of the SQLite shell:
#
# 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
# 0|1|1|SCAN TABLE posts (~100000 rows)
#
def pp(result) # :nodoc:
result.rows.map do |row|
row.join('|')
end.join("\n") + "\n"
end
end
def exec_query(sql, name = nil, binds = [])
log(sql, name, binds) do
# Don't cache statements without bind values
if binds.empty?
stmt = @connection.prepare(sql)
cols = stmt.columns
records = stmt.to_a
stmt.close
stmt = records
else
cache = @statements[sql] ||= {
:stmt => @connection.prepare(sql)
}
stmt = cache[:stmt]
cols = cache[:cols] ||= stmt.columns
stmt.reset!
stmt.bind_params binds.map { |col, val|
type_cast(val, col)
}
end
ActiveRecord::Result.new(cols, stmt.to_a)
end
end
def exec_delete(sql, name = 'SQL', binds = [])
exec_query(sql, name, binds)
@connection.changes
end
alias :exec_update :exec_delete
def last_inserted_id(result)
@connection.last_insert_row_id
end
def execute(sql, name = nil) #:nodoc:
log(sql, name) { @connection.execute(sql) }
end
def update_sql(sql, name = nil) #:nodoc:
super
@connection.changes
end
def delete_sql(sql, name = nil) #:nodoc:
sql += " WHERE 1=1" unless sql =~ /WHERE/i
super sql, name
end
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
super
id_value || @connection.last_insert_row_id
end
alias :create :insert_sql
def select_rows(sql, name = nil)
exec_query(sql, name).rows
end
def create_savepoint
execute("SAVEPOINT #{current_savepoint_name}")
end
def rollback_to_savepoint
execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
end
def release_savepoint
execute("RELEASE SAVEPOINT #{current_savepoint_name}")
end
def begin_db_transaction #:nodoc:
log('begin transaction',nil) { @connection.transaction }
end
def commit_db_transaction #:nodoc:
log('commit transaction',nil) { @connection.commit }
end
def rollback_db_transaction #:nodoc:
log('rollback transaction',nil) { @connection.rollback }
end
# SCHEMA STATEMENTS ========================================
def tables(name = 'SCHEMA', table_name = nil) #:nodoc:
sql = <<-SQL
SELECT name
FROM sqlite_master
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
SQL
sql << " AND name = #{quote_table_name(table_name)}" if table_name
exec_query(sql, name).map do |row|
row['name']
end
end
def table_exists?(name)
name && tables('SCHEMA', name).any?
end
# Returns an array of +SQLite3Column+ objects for the table specified by +table_name+.
def columns(table_name) #:nodoc:
table_structure(table_name).map do |field|
case field["dflt_value"]
when /^null$/i
field["dflt_value"] = nil
when /^'(.*)'$/
field["dflt_value"] = $1.gsub("''", "'")
when /^"(.*)"$/
field["dflt_value"] = $1.gsub('""', '"')
end
SQLite3Column.new(field['name'], field['dflt_value'], field['type'], field['notnull'].to_i == 0)
end
end
# Returns an array of indexes for the given table.
def indexes(table_name, name = nil) #:nodoc:
exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row|
IndexDefinition.new(
table_name,
row['name'],
row['unique'] != 0,
exec_query("PRAGMA index_info('#{row['name']}')").map { |col|
col['name']
})
end
end
def primary_key(table_name) #:nodoc:
column = table_structure(table_name).find { |field|
field['pk'] == 1
}
column && column['name']
end
def remove_index!(table_name, index_name) #:nodoc:
exec_query "DROP INDEX #{quote_column_name(index_name)}"
end
# Renames a table.
#
# Example:
# rename_table('octopuses', 'octopi')
def rename_table(name, new_name)
exec_query "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
end
# See: http://www.sqlite.org/lang_altertable.html
# SQLite has an additional restriction on the ALTER TABLE statement
def valid_alter_table_options( type, options)
type.to_sym != :primary_key
end
def add_column(table_name, column_name, type, options = {}) #:nodoc:
if supports_add_column? && valid_alter_table_options( type, options )
super(table_name, column_name, type, options)
else
alter_table(table_name) do |definition|
definition.column(column_name, type, options)
end
end
end
def remove_column(table_name, *column_names) #:nodoc:
raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
column_names.flatten.each do |column_name|
alter_table(table_name) do |definition|
definition.columns.delete(definition[column_name])
end
end
end
alias :remove_columns :remove_column
def change_column_default(table_name, column_name, default) #:nodoc:
alter_table(table_name) do |definition|
definition[column_name].default = default
end
end
def change_column_null(table_name, column_name, null, default = nil)
unless null || default.nil?
exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
end
alter_table(table_name) do |definition|
definition[column_name].null = null
end
end
def change_column(table_name, column_name, type, options = {}) #:nodoc:
alter_table(table_name) do |definition|
include_default = options_include_default?(options)
definition[column_name].instance_eval do
self.type = type
self.limit = options[:limit] if options.include?(:limit)
self.default = options[:default] if include_default
self.null = options[:null] if options.include?(:null)
self.precision = options[:precision] if options.include?(:precision)
self.scale = options[:scale] if options.include?(:scale)
end
end
end
def rename_column(table_name, column_name, new_column_name) #:nodoc:
unless columns(table_name).detect{|c| c.name == column_name.to_s }
raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
end
alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
end
def empty_insert_statement_value
"VALUES(NULL)"
end
protected
def select(sql, name = nil, binds = []) #:nodoc:
exec_query(sql, name, binds)
end
def table_structure(table_name)
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
structure
end
def alter_table(table_name, options = {}) #:nodoc:
altered_table_name = "altered_#{table_name}"
caller = lambda {|definition| yield definition if block_given?}
transaction do
move_table(table_name, altered_table_name,
options.merge(:temporary => true))
move_table(altered_table_name, table_name, &caller)
end
end
def move_table(from, to, options = {}, &block) #:nodoc:
copy_table(from, to, options, &block)
drop_table(from)
end
def copy_table(from, to, options = {}) #:nodoc:
options = options.merge(:id => (!columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s))
create_table(to, options) do |definition|
@definition = definition
columns(from).each do |column|
column_name = options[:rename] ?
(options[:rename][column.name] ||
options[:rename][column.name.to_sym] ||
column.name) : column.name
@definition.column(column_name, column.type,
:limit => column.limit, :default => column.default,
:precision => column.precision, :scale => column.scale,
:null => column.null)
end
@definition.primary_key(primary_key(from)) if primary_key(from)
yield @definition if block_given?
end
copy_table_indexes(from, to, options[:rename] || {})
copy_table_contents(from, to,
@definition.columns.map {|column| column.name},
options[:rename] || {})
end
def copy_table_indexes(from, to, rename = {}) #:nodoc:
indexes(from).each do |index|
name = index.name
if to == "altered_#{from}"
name = "temp_#{name}"
elsif from == "altered_#{to}"
name = name[5..-1]
end
to_column_names = columns(to).map { |c| c.name }
columns = index.columns.map {|c| rename[c] || c }.select do |column|
to_column_names.include?(column)
end
unless columns.empty?
# index name can't be the same
opts = { :name => name.gsub(/_(#{from})_/, "_#{to}_") }
opts[:unique] = true if index.unique
add_index(to, columns, opts)
end
end
end
def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
column_mappings = Hash[columns.map {|name| [name, name]}]
rename.each { |a| column_mappings[a.last] = a.first }
from_columns = columns(from).collect {|col| col.name}
columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
quoted_columns = columns.map { |col| quote_column_name(col) } * ','
quoted_to = quote_table_name(to)
exec_query("SELECT * FROM #{quote_table_name(from)}").each do |row|
sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
sql << columns.map {|col| quote row[column_mappings[col]]} * ', '
sql << ')'
exec_query sql
end
end
def sqlite_version
@sqlite_version ||= SQLite3Adapter::Version.new(select_value('select sqlite_version(*)'))
end
def default_primary_key_type
if supports_autoincrement?
'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'
else
'INTEGER PRIMARY KEY NOT NULL'
end
end
def translate_exception(exception, message)
case exception.message
when /column(s)? .* (is|are) not unique/
RecordNotUnique.new(message, exception)
else
super
end
end
end
end
......
require 'active_record/connection_adapters/abstract_adapter'
require 'active_record/connection_adapters/statement_pool'
require 'arel/visitors/bind_visitor'
module ActiveRecord
module ConnectionAdapters #:nodoc:
class SQLiteColumn < Column #:nodoc:
class << self
def binary_to_string(value)
if value.encoding != Encoding::ASCII_8BIT
value = value.force_encoding(Encoding::ASCII_8BIT)
end
value
end
end
end
# The SQLite adapter works with both the 2.x and 3.x series of SQLite with the sqlite-ruby
# drivers (available both as gems and from http://rubyforge.org/projects/sqlite-ruby/).
#
# Options:
#
# * <tt>:database</tt> - Path to the database file.
class SQLiteAdapter < AbstractAdapter
class Version
include Comparable
def initialize(version_string)
@version = version_string.split('.').map { |v| v.to_i }
end
def <=>(version_string)
@version <=> version_string.split('.').map { |v| v.to_i }
end
end
class StatementPool < ConnectionAdapters::StatementPool
def initialize(connection, max)
super
@cache = Hash.new { |h,pid| h[pid] = {} }
end
def each(&block); cache.each(&block); end
def key?(key); cache.key?(key); end
def [](key); cache[key]; end
def length; cache.length; end
def []=(sql, key)
while @max <= cache.size
dealloc(cache.shift.last[:stmt])
end
cache[sql] = key
end
def clear
cache.values.each do |hash|
dealloc hash[:stmt]
end
cache.clear
end
private
def cache
@cache[$$]
end
def dealloc(stmt)
stmt.close unless stmt.closed?
end
end
class BindSubstitution < Arel::Visitors::SQLite # :nodoc:
include Arel::Visitors::BindVisitor
end
def initialize(connection, logger, config)
super(connection, logger)
@statements = StatementPool.new(@connection,
config.fetch(:statement_limit) { 1000 })
@config = config
if config.fetch(:prepared_statements) { true }
@visitor = Arel::Visitors::SQLite.new self
else
@visitor = BindSubstitution.new self
end
end
def adapter_name #:nodoc:
'SQLite'
end
# Returns true if SQLite version is '2.0.0' or greater, false otherwise.
def supports_ddl_transactions?
sqlite_version >= '2.0.0'
end
# Returns true if SQLite version is '3.6.8' or greater, false otherwise.
def supports_savepoints?
sqlite_version >= '3.6.8'
end
# Returns true, since this connection adapter supports prepared statement
# caching.
def supports_statement_cache?
true
end
# Returns true, since this connection adapter supports migrations.
def supports_migrations? #:nodoc:
true
end
# Returns true.
def supports_primary_key? #:nodoc:
true
end
# Returns true.
def supports_explain?
true
end
def requires_reloading?
true
end
# Returns true if SQLite version is '3.1.6' or greater, false otherwise.
def supports_add_column?
sqlite_version >= '3.1.6'
end
# Disconnects from the database if already connected. Otherwise, this
# method does nothing.
def disconnect!
super
clear_cache!
@connection.close rescue nil
end
# Clears the prepared statements cache.
def clear_cache!
@statements.clear
end
# Returns true if SQLite version is '3.2.6' or greater, false otherwise.
def supports_count_distinct? #:nodoc:
sqlite_version >= '3.2.6'
end
# Returns true if SQLite version is '3.1.0' or greater, false otherwise.
def supports_autoincrement? #:nodoc:
sqlite_version >= '3.1.0'
end
def supports_index_sort_order?
sqlite_version >= '3.3.0'
end
def native_database_types #:nodoc:
{
:primary_key => default_primary_key_type,
:string => { :name => "varchar", :limit => 255 },
:text => { :name => "text" },
:integer => { :name => "integer" },
:float => { :name => "float" },
:decimal => { :name => "decimal" },
:datetime => { :name => "datetime" },
:timestamp => { :name => "datetime" },
:time => { :name => "time" },
:date => { :name => "date" },
:binary => { :name => "blob" },
:boolean => { :name => "boolean" }
}
end
# QUOTING ==================================================
def quote_string(s) #:nodoc:
@connection.class.quote(s)
end
def quote_column_name(name) #:nodoc:
%Q("#{name.to_s.gsub('"', '""')}")
end
# Quote date/time values for use in SQL input. Includes microseconds
# if the value is a Time responding to usec.
def quoted_date(value) #:nodoc:
if value.respond_to?(:usec)
"#{super}.#{sprintf("%06d", value.usec)}"
else
super
end
end
def type_cast(value, column) # :nodoc:
return value.to_f if BigDecimal === value
return super unless String === value
return super unless column && value
value = super
if column.type == :string && value.encoding == Encoding::ASCII_8BIT
logger.error "Binary data inserted for `string` type on column `#{column.name}`" if logger
value.encode! 'utf-8'
end
value
end
# DATABASE STATEMENTS ======================================
def explain(arel, binds = [])
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
end
class ExplainPrettyPrinter
# Pretty prints the result of a EXPLAIN QUERY PLAN in a way that resembles
# the output of the SQLite shell:
#
# 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
# 0|1|1|SCAN TABLE posts (~100000 rows)
#
def pp(result) # :nodoc:
result.rows.map do |row|
row.join('|')
end.join("\n") + "\n"
end
end
def exec_query(sql, name = nil, binds = [])
log(sql, name, binds) do
# Don't cache statements without bind values
if binds.empty?
stmt = @connection.prepare(sql)
cols = stmt.columns
records = stmt.to_a
stmt.close
stmt = records
else
cache = @statements[sql] ||= {
:stmt => @connection.prepare(sql)
}
stmt = cache[:stmt]
cols = cache[:cols] ||= stmt.columns
stmt.reset!
stmt.bind_params binds.map { |col, val|
type_cast(val, col)
}
end
ActiveRecord::Result.new(cols, stmt.to_a)
end
end
def exec_delete(sql, name = 'SQL', binds = [])
exec_query(sql, name, binds)
@connection.changes
end
alias :exec_update :exec_delete
def last_inserted_id(result)
@connection.last_insert_row_id
end
def execute(sql, name = nil) #:nodoc:
log(sql, name) { @connection.execute(sql) }
end
def update_sql(sql, name = nil) #:nodoc:
super
@connection.changes
end
def delete_sql(sql, name = nil) #:nodoc:
sql += " WHERE 1=1" unless sql =~ /WHERE/i
super sql, name
end
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
super
id_value || @connection.last_insert_row_id
end
alias :create :insert_sql
def select_rows(sql, name = nil)
exec_query(sql, name).rows
end
def create_savepoint
execute("SAVEPOINT #{current_savepoint_name}")
end
def rollback_to_savepoint
execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
end
def release_savepoint
execute("RELEASE SAVEPOINT #{current_savepoint_name}")
end
def begin_db_transaction #:nodoc:
log('begin transaction',nil) { @connection.transaction }
end
def commit_db_transaction #:nodoc:
log('commit transaction',nil) { @connection.commit }
end
def rollback_db_transaction #:nodoc:
log('rollback transaction',nil) { @connection.rollback }
end
# SCHEMA STATEMENTS ========================================
def tables(name = 'SCHEMA', table_name = nil) #:nodoc:
sql = <<-SQL
SELECT name
FROM sqlite_master
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
SQL
sql << " AND name = #{quote_table_name(table_name)}" if table_name
exec_query(sql, name).map do |row|
row['name']
end
end
def table_exists?(name)
name && tables('SCHEMA', name).any?
end
# Returns an array of +SQLiteColumn+ objects for the table specified by +table_name+.
def columns(table_name) #:nodoc:
table_structure(table_name).map do |field|
case field["dflt_value"]
when /^null$/i
field["dflt_value"] = nil
when /^'(.*)'$/
field["dflt_value"] = $1.gsub("''", "'")
when /^"(.*)"$/
field["dflt_value"] = $1.gsub('""', '"')
end
SQLiteColumn.new(field['name'], field['dflt_value'], field['type'], field['notnull'].to_i == 0)
end
end
# Returns an array of indexes for the given table.
def indexes(table_name, name = nil) #:nodoc:
exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row|
IndexDefinition.new(
table_name,
row['name'],
row['unique'] != 0,
exec_query("PRAGMA index_info('#{row['name']}')").map { |col|
col['name']
})
end
end
def primary_key(table_name) #:nodoc:
column = table_structure(table_name).find { |field|
field['pk'] == 1
}
column && column['name']
end
def remove_index!(table_name, index_name) #:nodoc:
exec_query "DROP INDEX #{quote_column_name(index_name)}"
end
# Renames a table.
#
# Example:
# rename_table('octopuses', 'octopi')
def rename_table(name, new_name)
exec_query "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
end
# See: http://www.sqlite.org/lang_altertable.html
# SQLite has an additional restriction on the ALTER TABLE statement
def valid_alter_table_options( type, options)
type.to_sym != :primary_key
end
def add_column(table_name, column_name, type, options = {}) #:nodoc:
if supports_add_column? && valid_alter_table_options( type, options )
super(table_name, column_name, type, options)
else
alter_table(table_name) do |definition|
definition.column(column_name, type, options)
end
end
end
def remove_column(table_name, *column_names) #:nodoc:
raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
column_names.flatten.each do |column_name|
alter_table(table_name) do |definition|
definition.columns.delete(definition[column_name])
end
end
end
alias :remove_columns :remove_column
def change_column_default(table_name, column_name, default) #:nodoc:
alter_table(table_name) do |definition|
definition[column_name].default = default
end
end
def change_column_null(table_name, column_name, null, default = nil)
unless null || default.nil?
exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
end
alter_table(table_name) do |definition|
definition[column_name].null = null
end
end
def change_column(table_name, column_name, type, options = {}) #:nodoc:
alter_table(table_name) do |definition|
include_default = options_include_default?(options)
definition[column_name].instance_eval do
self.type = type
self.limit = options[:limit] if options.include?(:limit)
self.default = options[:default] if include_default
self.null = options[:null] if options.include?(:null)
self.precision = options[:precision] if options.include?(:precision)
self.scale = options[:scale] if options.include?(:scale)
end
end
end
def rename_column(table_name, column_name, new_column_name) #:nodoc:
unless columns(table_name).detect{|c| c.name == column_name.to_s }
raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
end
alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
end
def empty_insert_statement_value
"VALUES(NULL)"
end
protected
def select(sql, name = nil, binds = []) #:nodoc:
exec_query(sql, name, binds)
end
def table_structure(table_name)
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
structure
end
def alter_table(table_name, options = {}) #:nodoc:
altered_table_name = "altered_#{table_name}"
caller = lambda {|definition| yield definition if block_given?}
transaction do
move_table(table_name, altered_table_name,
options.merge(:temporary => true))
move_table(altered_table_name, table_name, &caller)
end
end
def move_table(from, to, options = {}, &block) #:nodoc:
copy_table(from, to, options, &block)
drop_table(from)
end
def copy_table(from, to, options = {}) #:nodoc:
options = options.merge(:id => (!columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s))
create_table(to, options) do |definition|
@definition = definition
columns(from).each do |column|
column_name = options[:rename] ?
(options[:rename][column.name] ||
options[:rename][column.name.to_sym] ||
column.name) : column.name
@definition.column(column_name, column.type,
:limit => column.limit, :default => column.default,
:precision => column.precision, :scale => column.scale,
:null => column.null)
end
@definition.primary_key(primary_key(from)) if primary_key(from)
yield @definition if block_given?
end
copy_table_indexes(from, to, options[:rename] || {})
copy_table_contents(from, to,
@definition.columns.map {|column| column.name},
options[:rename] || {})
end
def copy_table_indexes(from, to, rename = {}) #:nodoc:
indexes(from).each do |index|
name = index.name
if to == "altered_#{from}"
name = "temp_#{name}"
elsif from == "altered_#{to}"
name = name[5..-1]
end
to_column_names = columns(to).map { |c| c.name }
columns = index.columns.map {|c| rename[c] || c }.select do |column|
to_column_names.include?(column)
end
unless columns.empty?
# index name can't be the same
opts = { :name => name.gsub(/_(#{from})_/, "_#{to}_") }
opts[:unique] = true if index.unique
add_index(to, columns, opts)
end
end
end
def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
column_mappings = Hash[columns.map {|name| [name, name]}]
rename.each { |a| column_mappings[a.last] = a.first }
from_columns = columns(from).collect {|col| col.name}
columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
quoted_columns = columns.map { |col| quote_column_name(col) } * ','
quoted_to = quote_table_name(to)
exec_query("SELECT * FROM #{quote_table_name(from)}").each do |row|
sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
sql << columns.map {|col| quote row[column_mappings[col]]} * ', '
sql << ')'
exec_query sql
end
end
def sqlite_version
@sqlite_version ||= SQLiteAdapter::Version.new(select_value('select sqlite_version(*)'))
end
def default_primary_key_type
if supports_autoincrement?
'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'
else
'INTEGER PRIMARY KEY NOT NULL'
end
end
def translate_exception(exception, message)
case exception.message
when /column(s)? .* (is|are) not unique/
RecordNotUnique.new(message, exception)
else
super
end
end
end
end
end
......@@ -69,7 +69,7 @@ def update_counters(id, counters)
"#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
end
update_all(updates.join(', '), primary_key => id)
where(primary_key => id).update_all updates.join(', ')
end
# Increment a number field by one, usually representing a count.
......
......@@ -70,7 +70,7 @@ def exec_explain(queries) # :nodoc:
# the threshold is set to 0.
#
# As the name of the method suggests this only applies to automatic
# EXPLAINs, manual calls to +ActiveRecord::Relation#explain+ run.
# EXPLAINs, manual calls to <tt>ActiveRecord::Relation#explain</tt> run.
def silence_auto_explain
current = Thread.current
original, current[:available_queries_for_explain] = current[:available_queries_for_explain], false
......
......@@ -178,7 +178,7 @@ def update_column(name, value)
verify_readonly_attribute(name)
raise ActiveRecordError, "can not update on a new record object" unless persisted?
raw_write_attribute(name, value)
self.class.update_all({ name => value }, self.class.primary_key => id) == 1
self.class.where(self.class.primary_key => id).update_all(name => value) == 1
end
# Updates the attributes of the model from the passed-in hash and saves the
......@@ -313,7 +313,7 @@ def touch(name = nil)
@changed_attributes.except!(*changes.keys)
primary_key = self.class.primary_key
self.class.unscoped.update_all(changes, { primary_key => self[primary_key] }) == 1
self.class.unscoped.where(primary_key => self[primary_key]).update_all(changes) == 1
end
end
......
......@@ -236,7 +236,10 @@ def many?
# Please check unscoped if you want to remove all previous scopes (including
# the default_scope) during the execution of a block.
def scoping
@klass.with_scope(self, :overwrite) { yield }
previous, klass.current_scope = klass.current_scope, self
yield
ensure
klass.current_scope = previous
end
# Updates all records with details given if they match a set of conditions supplied, limits and order can
......
......@@ -3,32 +3,41 @@
module ActiveRecord
class Relation
class Merger
attr_reader :relation, :other
class HashMerger
attr_reader :relation, :hash
def initialize(relation, other)
@relation = relation
def initialize(relation, hash)
hash.assert_valid_keys(*Relation::VALUE_METHODS)
if other.default_scoped? && other.klass != relation.klass
@other = other.with_default_scope
else
@other = other
end
@relation = relation
@hash = hash
end
def merge
HashMerger.new(relation, other.values).merge
Merger.new(relation, other).merge
end
# Applying values to a relation has some side effects. E.g.
# interpolation might take place for where values. So we should
# build a relation to merge in rather than directly merging
# the values.
def other
other = Relation.new(relation.klass, relation.table)
hash.each { |k, v| other.send("#{k}!", v) }
other
end
end
class HashMerger
class Merger
attr_reader :relation, :values
def initialize(relation, values)
values.assert_valid_keys(*Relation::VALUE_METHODS)
def initialize(relation, other)
if other.default_scoped? && other.klass != relation.klass
other = other.with_default_scope
end
@relation = relation
@values = values
@values = other.values
end
def normal_values
......
......@@ -30,11 +30,8 @@ def merge(other)
end
def merge!(other)
if other.is_a?(Hash)
Relation::HashMerger.new(self, other).merge
else
Relation::Merger.new(self, other).merge
end
klass = other.is_a?(Hash) ? Relation::HashMerger : Relation::Merger
klass.new(self, other).merge
end
# Removes from the query the condition(s) specified in +skips+.
......
......@@ -10,118 +10,6 @@ module Scoping
end
module ClassMethods
# with_scope lets you apply options to inner block incrementally. It takes a hash and the keys must be
# <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameter is <tt>Relation</tt> while
# <tt>:create</tt> parameters are an attributes hash.
#
# class Article < ActiveRecord::Base
# def self.create_with_scope
# with_scope(:find => where(:blog_id => 1), :create => { :blog_id => 1 }) do
# find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
# a = create(1)
# a.blog_id # => 1
# end
# end
# end
#
# In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of
# <tt>where</tt>, <tt>includes</tt>, and <tt>joins</tt> operations in <tt>Relation</tt>, which are merged.
#
# <tt>joins</tt> operations are uniqued so multiple scopes can join in the same table without table aliasing
# problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the
# array of strings format for your joins.
#
# class Article < ActiveRecord::Base
# def self.find_with_scope
# with_scope(:find => where(:blog_id => 1).limit(1), :create => { :blog_id => 1 }) do
# with_scope(:find => limit(10)) do
# all # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
# end
# with_scope(:find => where(:author_id => 3)) do
# all # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
# end
# end
# end
# end
#
# You can ignore any previous scopings by using the <tt>with_exclusive_scope</tt> method.
#
# class Article < ActiveRecord::Base
# def self.find_with_exclusive_scope
# with_scope(:find => where(:blog_id => 1).limit(1)) do
# with_exclusive_scope(:find => limit(10)) do
# all # => SELECT * from articles LIMIT 10
# end
# end
# end
# end
#
# *Note*: the +:find+ scope also has effect on update and deletion methods, like +update_all+ and +delete_all+.
def with_scope(scope = {}, action = :merge, &block)
# If another Active Record class has been passed in, get its current scope
scope = scope.current_scope if !scope.is_a?(Relation) && scope.respond_to?(:current_scope)
previous_scope = self.current_scope
if scope.is_a?(Hash)
# Dup first and second level of hash (method and params).
scope = scope.dup
scope.each do |method, params|
scope[method] = params.dup unless params == true
end
scope.assert_valid_keys([ :find, :create ])
relation = construct_finder_arel(scope[:find] || {})
relation.default_scoped = true unless action == :overwrite
if previous_scope && previous_scope.create_with_value && scope[:create]
scope_for_create = if action == :merge
previous_scope.create_with_value.merge(scope[:create])
else
scope[:create]
end
relation = relation.create_with(scope_for_create)
else
scope_for_create = scope[:create]
scope_for_create ||= previous_scope.create_with_value if previous_scope
relation = relation.create_with(scope_for_create) if scope_for_create
end
scope = relation
end
scope = previous_scope.merge(scope) if previous_scope && action == :merge
self.current_scope = scope
begin
yield
ensure
self.current_scope = previous_scope
end
end
protected
# Works like with_scope, but discards any nested properties.
def with_exclusive_scope(method_scoping = {}, &block)
if method_scoping.values.any? { |e| e.is_a?(ActiveRecord::Relation) }
raise ArgumentError, <<-MSG
New finder API can not be used with_exclusive_scope. You can either call unscoped to get an anonymous scope not bound to the default_scope:
User.unscoped.where(:active => true)
Or call unscoped with a block:
User.unscoped do
User.where(:active => true).all
end
MSG
end
with_scope(method_scoping, :overwrite, &block)
end
def current_scope #:nodoc:
Thread.current["#{self}_current_scope"]
end
......@@ -129,15 +17,6 @@ def current_scope #:nodoc:
def current_scope=(scope) #:nodoc:
Thread.current["#{self}_current_scope"] = scope
end
private
def construct_finder_arel(options = {}, scope = nil)
relation = options.is_a?(Hash) ? unscoped.apply_finder_options(options) : options
relation = scope.merge(relation) if scope
relation
end
end
def populate_with_current_scope_attributes
......
......@@ -87,7 +87,7 @@ def before_remove_const #:nodoc:
# # Should return a scope, you can call 'super' here etc.
# end
# end
def default_scope(scope = {})
def default_scope(scope = nil)
scope = Proc.new if block_given?
if scope.is_a?(Relation) || !scope.respond_to?(:call)
......@@ -103,14 +103,13 @@ def default_scope(scope = {})
end
def build_default_scope #:nodoc:
if method(:default_scope).owner != ActiveRecord::Scoping::Default::ClassMethods
if !Base.is_a?(method(:default_scope).owner)
# The user has defined their own default scope method, so call that
evaluate_default_scope { default_scope }
elsif default_scopes.any?
evaluate_default_scope do
default_scopes.inject(relation) do |default_scope, scope|
if scope.is_a?(Hash)
default_scope.apply_finder_options(scope)
elsif !scope.is_a?(Relation) && scope.respond_to?(:call)
if !scope.is_a?(Relation) && scope.respond_to?(:call)
default_scope.merge(unscoped { scope.call })
else
default_scope.merge(scope)
......
......@@ -29,17 +29,16 @@ module ClassMethods
# You can define a \scope that applies to all finders using
# ActiveRecord::Base.default_scope.
def scoped(options = nil)
if options
scoped.apply_finder_options(options)
if current_scope
scope = current_scope.clone
else
if current_scope
current_scope.clone
else
scope = relation
scope.default_scoped = true
scope
end
scope = relation
scope.default_scoped = true
scope
end
scope.merge!(options) if options
scope
end
##
......@@ -172,7 +171,7 @@ def scope_attributes? # :nodoc:
# Article.published.featured.latest_article
# Article.featured.titles
def scope(name, body = {}, &block)
def scope(name, body, &block)
extension = Module.new(&block) if block
# Check body.is_a?(Relation) to prevent the relation actually being
......@@ -189,9 +188,7 @@ def scope(name, body = {}, &block)
end
singleton_class.send(:define_method, name) do |*args|
options = body.respond_to?(:call) ? unscoped { body.call(*args) } : body
options = scoped.apply_finder_options(options) if options.is_a?(Hash)
options = body.respond_to?(:call) ? unscoped { body.call(*args) } : body
relation = scoped.merge(options)
extension ? relation.extending(extension) : relation
......
......@@ -119,7 +119,7 @@ def self.find_by_session_id(*args)
class << self; remove_possible_method :find_by_session_id; end
def self.find_by_session_id(session_id)
find :first, :conditions => {:session_id=>session_id}
where(session_id: session_id).first
end
end
end
......
......@@ -196,7 +196,6 @@ module ClassMethods
# The following bundled adapters throw the ActiveRecord::RecordNotUnique exception:
# * ActiveRecord::ConnectionAdapters::MysqlAdapter
# * ActiveRecord::ConnectionAdapters::Mysql2Adapter
# * ActiveRecord::ConnectionAdapters::SQLiteAdapter
# * ActiveRecord::ConnectionAdapters::SQLite3Adapter
# * ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
#
......
......@@ -130,7 +130,7 @@ def test_calculations_work_with_reserved_words
end
def test_associations_work_with_reserved_words
assert_nothing_raised { Select.find(:all, :include => [:groups]) }
assert_nothing_raised { Select.scoped(:includes => [:groups]).all }
end
#the following functions were added to DRY test cases
......
......@@ -20,7 +20,7 @@ def self.name; 'Post'; end
end
def test_schema
assert @omgpost.find(:first)
assert @omgpost.first
end
def test_primary_key
......
......@@ -9,7 +9,7 @@ class BindParameterTest < ActiveRecord::TestCase
def test_update_question_marks
str = "foo?bar"
x = Topic.find :first
x = Topic.first
x.title = str
x.content = str
x.save!
......@@ -28,7 +28,7 @@ def test_create_question_marks
def test_update_null_bytes
str = "foo\0bar"
x = Topic.find :first
x = Topic.first
x.title = str
x.content = str
x.save!
......
......@@ -130,7 +130,7 @@ def test_calculations_work_with_reserved_words
end
def test_associations_work_with_reserved_words
assert_nothing_raised { Select.find(:all, :include => [:groups]) }
assert_nothing_raised { Select.scoped(:includes => [:groups]).all }
end
#the following functions were added to DRY test cases
......
......@@ -20,7 +20,7 @@ def self.name; 'Post'; end
end
def test_schema
assert @omgpost.find(:first)
assert @omgpost.first
end
def test_primary_key
......
# encoding: utf-8
require "cases/helper"
require 'active_record/base'
require 'active_record/connection_adapters/postgresql_adapter'
......@@ -88,7 +90,7 @@ def test_parse7
def test_rewrite
@connection.execute "insert into hstores (tags) VALUES ('1=>2')"
x = Hstore.find :first
x = Hstore.first
x.tags = { '"a\'' => 'b' }
assert x.save!
end
......@@ -96,13 +98,13 @@ def test_rewrite
def test_select
@connection.execute "insert into hstores (tags) VALUES ('1=>2')"
x = Hstore.find :first
x = Hstore.first
assert_equal({'1' => '2'}, x.tags)
end
def test_select_multikey
@connection.execute "insert into hstores (tags) VALUES ('1=>2,2=>3')"
x = Hstore.find :first
x = Hstore.first
assert_equal({'1' => '2', '2' => '3'}, x.tags)
end
......@@ -134,13 +136,19 @@ def test_arrow
assert_cycle('a=>b' => 'bar', '1"foo' => '2')
end
def test_quoting_special_characters
assert_cycle('ca' => 'cà', 'ac' => 'àc')
end
private
def assert_cycle hash
# test creation
x = Hstore.create!(:tags => hash)
x.reload
assert_equal(hash, x.tags)
# make sure updates work
# test updating
x = Hstore.create!(:tags => {})
x.tags = hash
x.save!
x.reload
......
......@@ -49,6 +49,33 @@ def test_insert_sql_with_no_space_after_table_name
assert_equal expect, id
end
def test_insert_sql_with_returning_disabled
connection = connection_without_insert_returning
id = connection.insert_sql("insert into postgresql_partitioned_table_parent (number) VALUES (1)")
expect = connection.query('select max(id) from postgresql_partitioned_table_parent').first.first
assert_equal expect, id
end
def test_exec_insert_with_returning_disabled
connection = connection_without_insert_returning
result = connection.exec_insert("insert into postgresql_partitioned_table_parent (number) VALUES (1)", nil, [], 'id', 'postgresql_partitioned_table_parent_id_seq')
expect = connection.query('select max(id) from postgresql_partitioned_table_parent').first.first
assert_equal expect, result.rows.first.first
end
def test_exec_insert_with_returning_disabled_and_no_sequence_name_given
connection = connection_without_insert_returning
result = connection.exec_insert("insert into postgresql_partitioned_table_parent (number) VALUES (1)", nil, [], 'id')
expect = connection.query('select max(id) from postgresql_partitioned_table_parent').first.first
assert_equal expect, result.rows.first.first
end
def test_sql_for_insert_with_returning_disabled
connection = connection_without_insert_returning
result = connection.sql_for_insert('sql', nil, nil, nil, 'binds')
assert_equal ['sql', 'binds'], result
end
def test_serial_sequence
assert_equal 'public.accounts_id_seq',
@connection.serial_sequence('accounts', 'id')
......@@ -204,6 +231,10 @@ def insert(ctx, data)
ctx.exec_insert(sql, 'SQL', binds)
end
def connection_without_insert_returning
ActiveRecord::Base.postgresql_connection(ActiveRecord::Base.configurations['arunit'].merge(:insert_returning => false))
end
end
end
end
......@@ -5,7 +5,7 @@
module ActiveRecord
module ConnectionAdapters
class SQLiteAdapter
class SQLite3Adapter
class QuotingTest < ActiveRecord::TestCase
def setup
@conn = Base.sqlite3_connection :database => ':memory:',
......
require 'cases/helper'
module ActiveRecord::ConnectionAdapters
class SQLiteAdapter
class SQLite3Adapter
class StatementPoolTest < ActiveRecord::TestCase
def test_cache_is_per_pid
return skip('must support fork') unless Process.respond_to?(:fork)
......
......@@ -73,14 +73,14 @@ def test_natural_assignment_with_primary_key
def test_eager_loading_with_primary_key
Firm.create("name" => "Apple")
Client.create("name" => "Citibank", :firm_name => "Apple")
citibank_result = Client.find(:first, :conditions => {:name => "Citibank"}, :include => :firm_with_primary_key)
citibank_result = Client.scoped(:where => {:name => "Citibank"}, :includes => :firm_with_primary_key).first
assert citibank_result.association_cache.key?(:firm_with_primary_key)
end
def test_eager_loading_with_primary_key_as_symbol
Firm.create("name" => "Apple")
Client.create("name" => "Citibank", :firm_name => "Apple")
citibank_result = Client.find(:first, :conditions => {:name => "Citibank"}, :include => :firm_with_primary_key_symbols)
citibank_result = Client.scoped(:where => {:name => "Citibank"}, :includes => :firm_with_primary_key_symbols).first
assert citibank_result.association_cache.key?(:firm_with_primary_key_symbols)
end
......@@ -182,7 +182,7 @@ def test_with_polymorphic_and_condition
def test_with_select
assert_equal Company.find(2).firm_with_select.attributes.size, 1
assert_equal Company.find(2, :include => :firm_with_select ).firm_with_select.attributes.size, 1
assert_equal Company.scoped(:includes => :firm_with_select ).find(2).firm_with_select.attributes.size, 1
end
def test_belongs_to_counter
......@@ -334,7 +334,7 @@ def test_assignment_before_child_saved_with_primary_key
def test_new_record_with_foreign_key_but_no_object
c = Client.new("firm_id" => 1)
# sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
assert_equal Firm.find(:first, :order => "id"), c.firm_with_basic_id
assert_equal Firm.scoped(:order => "id").first, c.firm_with_basic_id
end
def test_setting_foreign_key_after_nil_target_loaded
......@@ -394,9 +394,9 @@ def test_custom_counter_cache
end
def test_association_assignment_sticks
post = Post.find(:first)
post = Post.first
author1, author2 = Author.find(:all, :limit => 2)
author1, author2 = Author.scoped(:limit => 2).all
assert_not_nil author1
assert_not_nil author2
......@@ -498,14 +498,14 @@ def test_save_of_record_with_loaded_belongs_to
assert_nothing_raised do
Account.find(@account.id).save!
Account.find(@account.id, :include => :firm).save!
Account.scoped(:includes => :firm).find(@account.id).save!
end
@account.firm.delete
assert_nothing_raised do
Account.find(@account.id).save!
Account.find(@account.id, :include => :firm).save!
Account.scoped(:includes => :firm).find(@account.id).save!
end
end
......@@ -705,4 +705,27 @@ def test_polymorphic_with_custom_primary_key
assert_equal toy, sponsor.reload.sponsorable
end
test "stale tracking doesn't care about the type" do
apple = Firm.create("name" => "Apple")
citibank = Account.create("credit_limit" => 10)
citibank.firm_id = apple.id
citibank.firm # load it
citibank.firm_id = apple.id.to_s
assert !citibank.association(:firm).stale_target?
end
def test_reflect_the_most_recent_change
author1, author2 = Author.limit(2)
post = Post.new(:title => "foo", :body=> "bar")
post.author = author1
post.author_id = author2.id
assert post.save
assert_equal post.author_id, author2.id
end
end
......@@ -16,7 +16,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
:categorizations, :people, :categories, :edges, :vertices
def test_eager_association_loading_with_cascaded_two_levels
authors = Author.find(:all, :include=>{:posts=>:comments}, :order=>"authors.id")
authors = Author.scoped(:includes=>{:posts=>:comments}, :order=>"authors.id").all
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
assert_equal 3, authors[1].posts.size
......@@ -24,7 +24,7 @@ def test_eager_association_loading_with_cascaded_two_levels
end
def test_eager_association_loading_with_cascaded_two_levels_and_one_level
authors = Author.find(:all, :include=>[{:posts=>:comments}, :categorizations], :order=>"authors.id")
authors = Author.scoped(:includes=>[{:posts=>:comments}, :categorizations], :order=>"authors.id").all
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
assert_equal 3, authors[1].posts.size
......@@ -84,7 +84,7 @@ def test_eager_association_loading_with_join_for_count
end
def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations
authors = Author.find(:all, :include=>{:posts=>[:comments, :categorizations]}, :order=>"authors.id")
authors = Author.scoped(:includes=>{:posts=>[:comments, :categorizations]}, :order=>"authors.id").all
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
assert_equal 3, authors[1].posts.size
......@@ -92,7 +92,7 @@ def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_as
end
def test_eager_association_loading_with_cascaded_two_levels_and_self_table_reference
authors = Author.find(:all, :include=>{:posts=>[:comments, :author]}, :order=>"authors.id")
authors = Author.scoped(:includes=>{:posts=>[:comments, :author]}, :order=>"authors.id").all
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
assert_equal authors(:david).name, authors[0].name
......@@ -100,13 +100,13 @@ def test_eager_association_loading_with_cascaded_two_levels_and_self_table_refer
end
def test_eager_association_loading_with_cascaded_two_levels_with_condition
authors = Author.find(:all, :include=>{:posts=>:comments}, :conditions=>"authors.id=1", :order=>"authors.id")
authors = Author.scoped(:includes=>{:posts=>:comments}, :where=>"authors.id=1", :order=>"authors.id").all
assert_equal 1, authors.size
assert_equal 5, authors[0].posts.size
end
def test_eager_association_loading_with_cascaded_three_levels_by_ping_pong
firms = Firm.find(:all, :include=>{:account=>{:firm=>:account}}, :order=>"companies.id")
firms = Firm.scoped(:includes=>{:account=>{:firm=>:account}}, :order=>"companies.id").all
assert_equal 2, firms.size
assert_equal firms.first.account, firms.first.account.firm.account
assert_equal companies(:first_firm).account, assert_no_queries { firms.first.account.firm.account }
......@@ -114,7 +114,7 @@ def test_eager_association_loading_with_cascaded_three_levels_by_ping_pong
end
def test_eager_association_loading_with_has_many_sti
topics = Topic.find(:all, :include => :replies, :order => 'topics.id')
topics = Topic.scoped(:includes => :replies, :order => 'topics.id').all
first, second, = topics(:first).replies.size, topics(:second).replies.size
assert_no_queries do
assert_equal first, topics[0].replies.size
......@@ -127,7 +127,7 @@ def test_eager_association_loading_with_has_many_sti_and_subclasses
silly.parent_id = 1
assert silly.save
topics = Topic.find(:all, :include => :replies, :order => ['topics.id', 'replies_topics.id'])
topics = Topic.scoped(:includes => :replies, :order => ['topics.id', 'replies_topics.id']).all
assert_no_queries do
assert_equal 2, topics[0].replies.size
assert_equal 0, topics[1].replies.size
......@@ -135,14 +135,14 @@ def test_eager_association_loading_with_has_many_sti_and_subclasses
end
def test_eager_association_loading_with_belongs_to_sti
replies = Reply.find(:all, :include => :topic, :order => 'topics.id')
replies = Reply.scoped(:includes => :topic, :order => 'topics.id').all
assert replies.include?(topics(:second))
assert !replies.include?(topics(:first))
assert_equal topics(:first), assert_no_queries { replies.first.topic }
end
def test_eager_association_loading_with_multiple_stis_and_order
author = Author.find(:first, :include => { :posts => [ :special_comments , :very_special_comment ] }, :order => ['authors.name', 'comments.body', 'very_special_comments_posts.body'], :conditions => 'posts.id = 4')
author = Author.scoped(:includes => { :posts => [ :special_comments , :very_special_comment ] }, :order => ['authors.name', 'comments.body', 'very_special_comments_posts.body'], :where => 'posts.id = 4').first
assert_equal authors(:david), author
assert_no_queries do
author.posts.first.special_comments
......@@ -151,7 +151,7 @@ def test_eager_association_loading_with_multiple_stis_and_order
end
def test_eager_association_loading_of_stis_with_multiple_references
authors = Author.find(:all, :include => { :posts => { :special_comments => { :post => [ :special_comments, :very_special_comment ] } } }, :order => 'comments.body, very_special_comments_posts.body', :conditions => 'posts.id = 4')
authors = Author.scoped(:includes => { :posts => { :special_comments => { :post => [ :special_comments, :very_special_comment ] } } }, :order => 'comments.body, very_special_comments_posts.body', :where => 'posts.id = 4').all
assert_equal [authors(:david)], authors
assert_no_queries do
authors.first.posts.first.special_comments.first.post.special_comments
......@@ -160,7 +160,7 @@ def test_eager_association_loading_of_stis_with_multiple_references
end
def test_eager_association_loading_where_first_level_returns_nil
authors = Author.find(:all, :include => {:post_about_thinking => :comments}, :order => 'authors.id DESC')
authors = Author.scoped(:includes => {:post_about_thinking => :comments}, :order => 'authors.id DESC').all
assert_equal [authors(:bob), authors(:mary), authors(:david)], authors
assert_no_queries do
authors[2].post_about_thinking.comments.first
......@@ -168,12 +168,12 @@ def test_eager_association_loading_where_first_level_returns_nil
end
def test_eager_association_loading_with_recursive_cascading_four_levels_has_many_through
source = Vertex.find(:first, :include=>{:sinks=>{:sinks=>{:sinks=>:sinks}}}, :order => 'vertices.id')
source = Vertex.scoped(:includes=>{:sinks=>{:sinks=>{:sinks=>:sinks}}}, :order => 'vertices.id').first
assert_equal vertices(:vertex_4), assert_no_queries { source.sinks.first.sinks.first.sinks.first }
end
def test_eager_association_loading_with_recursive_cascading_four_levels_has_and_belongs_to_many
sink = Vertex.find(:first, :include=>{:sources=>{:sources=>{:sources=>:sources}}}, :order => 'vertices.id DESC')
sink = Vertex.scoped(:includes=>{:sources=>{:sources=>{:sources=>:sources}}}, :order => 'vertices.id DESC').first
assert_equal vertices(:vertex_1), assert_no_queries { sink.sources.first.sources.first.sources.first.sources.first }
end
end
......@@ -24,11 +24,11 @@ def test_class_names
old = ActiveRecord::Base.store_full_sti_class
ActiveRecord::Base.store_full_sti_class = false
post = Namespaced::Post.find_by_title( 'Great stuff', :include => :tagging )
post = Namespaced::Post.includes(:tagging).find_by_title('Great stuff')
assert_nil post.tagging
ActiveRecord::Base.store_full_sti_class = true
post = Namespaced::Post.find_by_title( 'Great stuff', :include => :tagging )
post = Namespaced::Post.includes(:tagging).find_by_title('Great stuff')
assert_instance_of Tagging, post.tagging
ensure
ActiveRecord::Base.store_full_sti_class = old
......
......@@ -93,7 +93,7 @@ def generate_test_object_graphs
def test_include_query
res = 0
res = ShapeExpression.find :all, :include => [ :shape, { :paint => :non_poly } ]
res = ShapeExpression.scoped(:includes => [ :shape, { :paint => :non_poly } ]).all
assert_equal NUM_SHAPE_EXPRESSIONS, res.size
assert_queries(0) do
res.each do |se|
......@@ -123,7 +123,7 @@ def test_missing_data_in_a_nested_include_should_not_cause_errors_when_construct
assert_nothing_raised do
# @davey_mcdave doesn't have any author_favorites
includes = {:posts => :comments, :categorizations => :category, :author_favorites => :favorite_author }
Author.all :include => includes, :conditions => {:authors => {:name => @davey_mcdave.name}}, :order => 'categories.name'
Author.scoped(:includes => includes, :where => {:authors => {:name => @davey_mcdave.name}}, :order => 'categories.name').to_a
end
end
end
......@@ -103,43 +103,43 @@ def teardown
def test_eager_no_extra_singularization_belongs_to
return unless @have_tables
assert_nothing_raised do
Virus.find(:all, :include => :octopus)
Virus.scoped(:includes => :octopus).all
end
end
def test_eager_no_extra_singularization_has_one
return unless @have_tables
assert_nothing_raised do
Octopus.find(:all, :include => :virus)
Octopus.scoped(:includes => :virus).all
end
end
def test_eager_no_extra_singularization_has_many
return unless @have_tables
assert_nothing_raised do
Bus.find(:all, :include => :passes)
Bus.scoped(:includes => :passes).all
end
end
def test_eager_no_extra_singularization_has_and_belongs_to_many
return unless @have_tables
assert_nothing_raised do
Crisis.find(:all, :include => :messes)
Mess.find(:all, :include => :crises)
Crisis.scoped(:includes => :messes).all
Mess.scoped(:includes => :crises).all
end
end
def test_eager_no_extra_singularization_has_many_through_belongs_to
return unless @have_tables
assert_nothing_raised do
Crisis.find(:all, :include => :successes)
Crisis.scoped(:includes => :successes).all
end
end
def test_eager_no_extra_singularization_has_many_through_has_many
return unless @have_tables
assert_nothing_raised do
Crisis.find(:all, :include => :compresses)
Crisis.scoped(:includes => :compresses).all
end
end
end
......@@ -355,7 +355,7 @@ def test_deleting
def test_deleting_array
david = Developer.find(1)
david.projects.reload
david.projects.delete(Project.find(:all))
david.projects.delete(Project.all)
assert_equal 0, david.projects.size
assert_equal 0, david.projects(true).size
end
......@@ -375,7 +375,7 @@ def test_deleting_array_with_sql
active_record.developers.reload
assert_equal 3, active_record.developers_by_sql.size
active_record.developers_by_sql.delete(Developer.find(:all))
active_record.developers_by_sql.delete(Developer.all)
assert_equal 0, active_record.developers_by_sql(true).size
end
......@@ -548,15 +548,15 @@ def test_find_in_association_with_custom_finder_sql_and_string_id
def test_find_with_merged_options
assert_equal 1, projects(:active_record).limited_developers.size
assert_equal 1, projects(:active_record).limited_developers.find(:all).size
assert_equal 3, projects(:active_record).limited_developers.find(:all, :limit => nil).size
assert_equal 1, projects(:active_record).limited_developers.all.size
assert_equal 3, projects(:active_record).limited_developers.limit(nil).all.size
end
def test_dynamic_find_should_respect_association_order
# Developers are ordered 'name DESC, id DESC'
high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
assert_equal high_id_jamis, projects(:active_record).developers.find(:first, :conditions => "name = 'Jamis'")
assert_equal high_id_jamis, projects(:active_record).developers.scoped(:where => "name = 'Jamis'").first
assert_equal high_id_jamis, projects(:active_record).developers.find_by_name('Jamis')
end
......@@ -566,7 +566,7 @@ def test_dynamic_find_all_should_respect_association_order
middle_id_jamis = developers(:poor_jamis)
high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
assert_equal [high_id_jamis, middle_id_jamis, low_id_jamis], projects(:active_record).developers.find(:all, :conditions => "name = 'Jamis'")
assert_equal [high_id_jamis, middle_id_jamis, low_id_jamis], projects(:active_record).developers.scoped(:where => "name = 'Jamis'").all
assert_equal [high_id_jamis, middle_id_jamis, low_id_jamis], projects(:active_record).developers.find_all_by_name('Jamis')
end
......@@ -576,12 +576,12 @@ def test_find_should_append_to_association_order
end
def test_dynamic_find_all_should_respect_association_limit
assert_equal 1, projects(:active_record).limited_developers.find(:all, :conditions => "name = 'Jamis'").length
assert_equal 1, projects(:active_record).limited_developers.scoped(:where => "name = 'Jamis'").all.length
assert_equal 1, projects(:active_record).limited_developers.find_all_by_name('Jamis').length
end
def test_dynamic_find_all_order_should_override_association_limit
assert_equal 2, projects(:active_record).limited_developers.find(:all, :conditions => "name = 'Jamis'", :limit => 9_000).length
assert_equal 2, projects(:active_record).limited_developers.scoped(:where => "name = 'Jamis'", :limit => 9_000).all.length
assert_equal 2, projects(:active_record).limited_developers.find_all_by_name('Jamis', :limit => 9_000).length
end
......@@ -632,7 +632,7 @@ def test_replace_on_new_object
end
def test_consider_type
developer = Developer.find(:first)
developer = Developer.first
special_project = SpecialProject.create("name" => "Special Project")
other_project = developer.projects.first
......@@ -667,7 +667,7 @@ def test_habtm_respects_select
categories(:technology).select_testing_posts(true).each do |o|
assert_respond_to o, :correctness_marker
end
assert_respond_to categories(:technology).select_testing_posts.find(:first), :correctness_marker
assert_respond_to categories(:technology).select_testing_posts.first, :correctness_marker
end
def test_habtm_selects_all_columns_by_default
......@@ -681,10 +681,10 @@ def test_habtm_respects_select_query_method
def test_join_table_alias
assert_equal(
3,
Developer.references(:developers_projects_join).find(
:all, :include => {:projects => :developers},
:conditions => 'developers_projects_join.joined_on IS NOT NULL'
).size
Developer.references(:developers_projects_join).scoped(
:includes => {:projects => :developers},
:where => 'developers_projects_join.joined_on IS NOT NULL'
).to_a.size
)
end
......@@ -697,16 +697,16 @@ def test_join_with_group
assert_equal(
3,
Developer.references(:developers_projects_join).find(
:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL',
Developer.references(:developers_projects_join).scoped(
:includes => {:projects => :developers}, :where => 'developers_projects_join.joined_on IS NOT NULL',
:group => group.join(",")
).size
).to_a.size
)
end
def test_find_grouped
all_posts_from_category1 = Post.find(:all, :conditions => "category_id = 1", :joins => :categories)
grouped_posts_of_category1 = Post.find(:all, :conditions => "category_id = 1", :group => "author_id", :select => 'count(posts.id) as posts_count', :joins => :categories)
all_posts_from_category1 = Post.scoped(:where => "category_id = 1", :joins => :categories).all
grouped_posts_of_category1 = Post.scoped(:where => "category_id = 1", :group => "author_id", :select => 'count(posts.id) as posts_count', :joins => :categories).all
assert_equal 5, all_posts_from_category1.size
assert_equal 2, grouped_posts_of_category1.size
end
......@@ -780,8 +780,8 @@ def test_symbols_as_keys
assert_equal 1, project.developers.size
assert_equal 1, developer.projects.size
assert_equal developer, project.developers.find(:first)
assert_equal project, developer.projects.find(:first)
assert_equal developer, project.developers.first
assert_equal project, developer.projects.first
end
def test_self_referential_habtm_without_foreign_key_set_should_raise_exception
......@@ -798,13 +798,6 @@ def test_dynamic_find_should_respect_association_include
assert Category.find(1).posts_with_authors_sorted_by_author_id.find_by_title('Welcome to the weblog')
end
def test_counting_on_habtm_association_and_not_array
david = Developer.find(1)
# Extra parameter just to make sure we aren't falling back to
# Array#count in Ruby >=1.8.7, which would raise an ArgumentError
assert_nothing_raised { david.projects.count(:all, :conditions => '1=1') }
end
def test_count
david = Developer.find(1)
assert_equal 2, david.projects.count
......@@ -827,7 +820,7 @@ def test_count_with_finder_sql
def test_association_proxy_transaction_method_starts_transaction_in_association_class
Post.expects(:transaction)
Category.find(:first).posts.transaction do
Category.first.posts.transaction do
# nothing
end
end
......
......@@ -242,27 +242,19 @@ def force_signal37_to_load_all_clients_of_firm
# sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
def test_counting_with_counter_sql
assert_equal 2, Firm.find(:first, :order => "id").clients.count
assert_equal 2, Firm.scoped(:order => "id").first.clients.count
end
def test_counting
assert_equal 2, Firm.find(:first, :order => "id").plain_clients.count
end
def test_counting_with_empty_hash_conditions
assert_equal 2, Firm.find(:first, :order => "id").plain_clients.count(:conditions => {})
end
def test_counting_with_single_conditions
assert_equal 1, Firm.find(:first, :order => "id").plain_clients.count(:conditions => ['name=?', "Microsoft"])
assert_equal 2, Firm.scoped(:order => "id").first.plain_clients.count
end
def test_counting_with_single_hash
assert_equal 1, Firm.find(:first, :order => "id").plain_clients.count(:conditions => {:name => "Microsoft"})
assert_equal 1, Firm.scoped(:order => "id").first.plain_clients.where(:name => "Microsoft").count
end
def test_counting_with_column_name_and_hash
assert_equal 2, Firm.find(:first, :order => "id").plain_clients.count(:name)
assert_equal 2, Firm.scoped(:order => "id").first.plain_clients.count(:name)
end
def test_counting_with_association_limit
......@@ -272,7 +264,7 @@ def test_counting_with_association_limit
end
def test_finding
assert_equal 2, Firm.find(:first, :order => "id").clients.length
assert_equal 2, Firm.scoped(:order => "id").first.clients.length
end
def test_finding_array_compatibility
......@@ -281,14 +273,14 @@ def test_finding_array_compatibility
def test_find_with_blank_conditions
[[], {}, nil, ""].each do |blank|
assert_equal 2, Firm.find(:first, :order => "id").clients.find(:all, :conditions => blank).size
assert_equal 2, Firm.scoped(:order => "id").first.clients.where(blank).all.size
end
end
def test_find_many_with_merged_options
assert_equal 1, companies(:first_firm).limited_clients.size
assert_equal 1, companies(:first_firm).limited_clients.find(:all).size
assert_equal 2, companies(:first_firm).limited_clients.find(:all, :limit => nil).size
assert_equal 1, companies(:first_firm).limited_clients.all.size
assert_equal 2, companies(:first_firm).limited_clients.limit(nil).all.size
end
def test_find_should_append_to_association_order
......@@ -301,30 +293,25 @@ def test_dynamic_find_last_without_specified_order
end
def test_dynamic_find_should_respect_association_order
assert_equal companies(:second_client), companies(:first_firm).clients_sorted_desc.find(:first, :conditions => "type = 'Client'")
assert_equal companies(:second_client), companies(:first_firm).clients_sorted_desc.scoped(:where => "type = 'Client'").first
assert_equal companies(:second_client), companies(:first_firm).clients_sorted_desc.find_by_type('Client')
end
def test_dynamic_find_all_should_respect_association_order
assert_equal [companies(:second_client), companies(:first_client)], companies(:first_firm).clients_sorted_desc.find(:all, :conditions => "type = 'Client'")
assert_equal [companies(:second_client), companies(:first_client)], companies(:first_firm).clients_sorted_desc.scoped(:where => "type = 'Client'").all
assert_equal [companies(:second_client), companies(:first_client)], companies(:first_firm).clients_sorted_desc.find_all_by_type('Client')
end
def test_dynamic_find_all_should_respect_association_limit
assert_equal 1, companies(:first_firm).limited_clients.find(:all, :conditions => "type = 'Client'").length
assert_equal 1, companies(:first_firm).limited_clients.scoped(:where => "type = 'Client'").all.length
assert_equal 1, companies(:first_firm).limited_clients.find_all_by_type('Client').length
end
def test_dynamic_find_all_limit_should_override_association_limit
assert_equal 2, companies(:first_firm).limited_clients.find(:all, :conditions => "type = 'Client'", :limit => 9_000).length
assert_equal 2, companies(:first_firm).limited_clients.scoped(:where => "type = 'Client'", :limit => 9_000).all.length
assert_equal 2, companies(:first_firm).limited_clients.find_all_by_type('Client', :limit => 9_000).length
end
def test_dynamic_find_all_should_respect_readonly_access
companies(:first_firm).readonly_clients.find(:all).each { |c| assert_raise(ActiveRecord::ReadOnlyRecord) { c.save! } }
companies(:first_firm).readonly_clients.find(:all).each { |c| assert c.readonly? }
end
def test_dynamic_find_or_create_from_two_attributes_using_an_association
author = authors(:david)
number_of_posts = Post.count
......@@ -341,59 +328,59 @@ def test_cant_save_has_many_readonly_association
def test_triple_equality
# sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
assert !(Array === Firm.find(:first, :order => "id").clients)
assert Firm.find(:first, :order => "id").clients === Array
assert !(Array === Firm.scoped(:order => "id").first.clients)
assert Firm.scoped(:order => "id").first.clients === Array
end
def test_finding_default_orders
assert_equal "Summit", Firm.find(:first, :order => "id").clients.first.name
assert_equal "Summit", Firm.scoped(:order => "id").first.clients.first.name
end
def test_finding_with_different_class_name_and_order
assert_equal "Microsoft", Firm.find(:first, :order => "id").clients_sorted_desc.first.name
assert_equal "Microsoft", Firm.scoped(:order => "id").first.clients_sorted_desc.first.name
end
def test_finding_with_foreign_key
assert_equal "Microsoft", Firm.find(:first, :order => "id").clients_of_firm.first.name
assert_equal "Microsoft", Firm.scoped(:order => "id").first.clients_of_firm.first.name
end
def test_finding_with_condition
assert_equal "Microsoft", Firm.find(:first, :order => "id").clients_like_ms.first.name
assert_equal "Microsoft", Firm.scoped(:order => "id").first.clients_like_ms.first.name
end
def test_finding_with_condition_hash
assert_equal "Microsoft", Firm.find(:first, :order => "id").clients_like_ms_with_hash_conditions.first.name
assert_equal "Microsoft", Firm.scoped(:order => "id").first.clients_like_ms_with_hash_conditions.first.name
end
def test_finding_using_primary_key
assert_equal "Summit", Firm.find(:first, :order => "id").clients_using_primary_key.first.name
assert_equal "Summit", Firm.scoped(:order => "id").first.clients_using_primary_key.first.name
end
def test_finding_using_sql
firm = Firm.find(:first, :order => "id")
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.find(:first, :order => "id").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.find(:first, :order => "id")
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.find(:first, :order => "id").clients_using_counter_sql.size
assert Firm.find(:first, :order => "id").clients_using_counter_sql.any?
assert_equal 0, Firm.find(:first, :order => "id").clients_using_zero_counter_sql.size
assert !Firm.find(:first, :order => "id").clients_using_zero_counter_sql.any?
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.find(:first, :order => "id").no_clients_using_counter_sql.size
assert_equal 0, Firm.scoped(:order => "id").first.no_clients_using_counter_sql.size
end
def test_counting_using_finder_sql
......@@ -408,7 +395,7 @@ def test_belongs_to_sanity
end
def test_find_ids
firm = Firm.find(:first, :order => "id")
firm = Firm.scoped(:order => "id").first
assert_raise(ActiveRecord::RecordNotFound) { firm.clients.find }
......@@ -428,7 +415,7 @@ def test_find_ids
end
def test_find_string_ids_when_using_finder_sql
firm = Firm.find(:first, :order => "id")
firm = Firm.scoped(:order => "id").first
client = firm.clients_using_finder_sql.find("2")
assert_kind_of Client, client
......@@ -444,9 +431,9 @@ def test_find_string_ids_when_using_finder_sql
end
def test_find_all
firm = Firm.find(:first, :order => "id")
assert_equal 2, firm.clients.find(:all, :conditions => "#{QUOTED_TYPE} = 'Client'").length
assert_equal 1, firm.clients.find(:all, :conditions => "name = 'Summit'").length
firm = Firm.scoped(:order => "id").first
assert_equal 2, firm.clients.scoped(:where => "#{QUOTED_TYPE} = 'Client'").all.length
assert_equal 1, firm.clients.scoped(:where => "name = 'Summit'").all.length
end
def test_find_each
......@@ -465,7 +452,7 @@ def test_find_each_with_conditions
firm = companies(:first_firm)
assert_queries(2) do
firm.clients.find_each(:batch_size => 1, :conditions => {:name => "Microsoft"}) do |c|
firm.clients.where(name: 'Microsoft').find_each(batch_size: 1) do |c|
assert_equal firm.id, c.firm_id
assert_equal "Microsoft", c.name
end
......@@ -490,29 +477,29 @@ def test_find_in_batches
def test_find_all_sanitized
# sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
firm = Firm.find(:first, :order => "id")
summit = firm.clients.find(:all, :conditions => "name = 'Summit'")
assert_equal summit, firm.clients.find(:all, :conditions => ["name = ?", "Summit"])
assert_equal summit, firm.clients.find(:all, :conditions => ["name = :name", { :name => "Summit" }])
firm = Firm.scoped(:order => "id").first
summit = firm.clients.scoped(:where => "name = 'Summit'").all
assert_equal summit, firm.clients.scoped(:where => ["name = ?", "Summit"]).all
assert_equal summit, firm.clients.scoped(:where => ["name = :name", { :name => "Summit" }]).all
end
def test_find_first
firm = Firm.find(:first, :order => "id")
firm = Firm.scoped(:order => "id").first
client2 = Client.find(2)
assert_equal firm.clients.first, firm.clients.find(:first, :order => "id")
assert_equal client2, firm.clients.find(:first, :conditions => "#{QUOTED_TYPE} = 'Client'", :order => "id")
assert_equal firm.clients.first, firm.clients.scoped(:order => "id").first
assert_equal client2, firm.clients.scoped(:where => "#{QUOTED_TYPE} = 'Client'", :order => "id").first
end
def test_find_first_sanitized
firm = Firm.find(:first, :order => "id")
firm = Firm.scoped(:order => "id").first
client2 = Client.find(2)
assert_equal client2, firm.clients.find(:first, :conditions => ["#{QUOTED_TYPE} = ?", 'Client'], :order => "id")
assert_equal client2, firm.clients.find(:first, :conditions => ["#{QUOTED_TYPE} = :type", { :type => 'Client' }], :order => "id")
assert_equal client2, firm.clients.scoped(:where => ["#{QUOTED_TYPE} = ?", 'Client'], :order => "id").first
assert_equal client2, firm.clients.scoped(:where => ["#{QUOTED_TYPE} = :type", { :type => 'Client' }], :order => "id").first
end
def test_find_all_with_include_and_conditions
assert_nothing_raised do
Developer.find(:all, :joins => :audit_logs, :conditions => {'audit_logs.message' => nil, :name => 'Smith'})
Developer.scoped(:joins => :audit_logs, :where => {'audit_logs.message' => nil, :name => 'Smith'}).all
end
end
......@@ -522,8 +509,8 @@ def test_find_in_collection
end
def test_find_grouped
all_clients_of_firm1 = Client.find(:all, :conditions => "firm_id = 1")
grouped_clients_of_firm1 = Client.find(:all, :conditions => "firm_id = 1", :group => "firm_id", :select => 'firm_id, count(id) as clients_count')
all_clients_of_firm1 = Client.scoped(:where => "firm_id = 1").all
grouped_clients_of_firm1 = Client.scoped(:where => "firm_id = 1", :group => "firm_id", :select => 'firm_id, count(id) as clients_count').all
assert_equal 2, all_clients_of_firm1.size
assert_equal 1, grouped_clients_of_firm1.size
end
......@@ -581,7 +568,7 @@ def test_regular_create_on_has_many_when_parent_is_new_raises
def test_create_with_bang_on_has_many_raises_when_record_not_saved
assert_raise(ActiveRecord::RecordInvalid) do
firm = Firm.find(:first, :order => "id")
firm = Firm.scoped(:order => "id").first
firm.plain_clients.create!
end
end
......@@ -813,7 +800,7 @@ def test_deleting_before_save
end
def test_deleting_updates_counter_cache
topic = Topic.first(:order => "id ASC")
topic = Topic.order("id ASC").first
assert_equal topic.replies.to_a.size, topic.replies_count
topic.replies.delete(topic.replies.first)
......@@ -1008,14 +995,14 @@ def test_dependent_association_respects_optional_hash_conditions_on_delete
end
def test_delete_all_association_with_primary_key_deletes_correct_records
firm = Firm.find(:first)
firm = Firm.first
# break the vanilla firm_id foreign key
assert_equal 2, firm.clients.count
firm.clients.first.update_column(:firm_id, nil)
assert_equal 1, firm.clients(true).count
assert_equal 1, firm.clients_using_primary_key_with_delete_all.count
old_record = firm.clients_using_primary_key_with_delete_all.first
firm = Firm.find(:first)
firm = Firm.first
firm.destroy
assert_nil Client.find_by_id(old_record.id)
end
......@@ -1123,7 +1110,7 @@ def test_dependence
firm = companies(:first_firm)
assert_equal 2, firm.clients.size
firm.destroy
assert Client.find(:all, :conditions => "firm_id=#{firm.id}").empty?
assert Client.scoped(:where => "firm_id=#{firm.id}").all.empty?
end
def test_dependence_for_associations_with_hash_condition
......@@ -1133,7 +1120,7 @@ def test_dependence_for_associations_with_hash_condition
def test_destroy_dependent_when_deleted_from_association
# sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
firm = Firm.find(:first, :order => "id")
firm = Firm.scoped(:order => "id").first
assert_equal 2, firm.clients.size
client = firm.clients.first
......@@ -1161,7 +1148,7 @@ def test_dependence_with_transaction_support_on_failure
firm.destroy rescue "do nothing"
assert_equal 2, Client.find(:all, :conditions => "firm_id=#{firm.id}").size
assert_equal 2, Client.scoped(:where => "firm_id=#{firm.id}").all.size
end
def test_dependence_on_account
......@@ -1224,16 +1211,11 @@ def test_included_in_collection
end
def test_adding_array_and_collection
assert_nothing_raised { Firm.find(:first).clients + Firm.find(:all).last.clients }
end
def test_find_all_without_conditions
firm = companies(:first_firm)
assert_equal 2, firm.clients.find(:all).length
assert_nothing_raised { Firm.first.clients + Firm.all.last.clients }
end
def test_replace_with_less
firm = Firm.find(:first, :order => "id")
firm = Firm.scoped(:order => "id").first
firm.clients = [companies(:first_client)]
assert firm.save, "Could not save firm"
firm.reload
......@@ -1247,7 +1229,7 @@ def test_replace_with_less_and_dependent_nullify
end
def test_replace_with_new
firm = Firm.find(:first, :order => "id")
firm = Firm.scoped(:order => "id").first
firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
firm.save
firm.reload
......@@ -1347,27 +1329,27 @@ def test_modifying_a_through_a_has_many_should_raise
end
def test_dynamic_find_should_respect_association_order_for_through
assert_equal Comment.find(10), authors(:david).comments_desc.find(:first, :conditions => "comments.type = 'SpecialComment'")
assert_equal Comment.find(10), authors(:david).comments_desc.scoped(:where => "comments.type = 'SpecialComment'").first
assert_equal Comment.find(10), authors(:david).comments_desc.find_by_type('SpecialComment')
end
def test_dynamic_find_all_should_respect_association_order_for_through
assert_equal [Comment.find(10), Comment.find(7), Comment.find(6), Comment.find(3)], authors(:david).comments_desc.find(:all, :conditions => "comments.type = 'SpecialComment'")
assert_equal [Comment.find(10), Comment.find(7), Comment.find(6), Comment.find(3)], authors(:david).comments_desc.scoped(:where => "comments.type = 'SpecialComment'").all
assert_equal [Comment.find(10), Comment.find(7), Comment.find(6), Comment.find(3)], authors(:david).comments_desc.find_all_by_type('SpecialComment')
end
def test_dynamic_find_all_should_respect_association_limit_for_through
assert_equal 1, authors(:david).limited_comments.find(:all, :conditions => "comments.type = 'SpecialComment'").length
assert_equal 1, authors(:david).limited_comments.scoped(:where => "comments.type = 'SpecialComment'").all.length
assert_equal 1, authors(:david).limited_comments.find_all_by_type('SpecialComment').length
end
def test_dynamic_find_all_order_should_override_association_limit_for_through
assert_equal 4, authors(:david).limited_comments.find(:all, :conditions => "comments.type = 'SpecialComment'", :limit => 9_000).length
assert_equal 4, authors(:david).limited_comments.scoped(:where => "comments.type = 'SpecialComment'", :limit => 9_000).all.length
assert_equal 4, authors(:david).limited_comments.find_all_by_type('SpecialComment', :limit => 9_000).length
end
def test_find_all_include_over_the_same_table_for_through
assert_equal 2, people(:michael).posts.find(:all, :include => :people).length
assert_equal 2, people(:michael).posts.scoped(:includes => :people).all.length
end
def test_has_many_through_respects_hash_conditions
......@@ -1499,17 +1481,6 @@ def test_blank_custom_primary_key_on_new_record_should_not_run_queries
end
end
def test_calling_first_or_last_with_find_options_on_loaded_association_should_fetch_with_query
firm = companies(:first_firm)
firm.clients.class # force load target
assert_queries 2 do
assert firm.clients.loaded?
firm.clients.first(:order => 'name')
firm.clients.last(:order => 'name')
end
end
def test_calling_first_or_last_with_integer_on_association_should_load_association
firm = companies(:first_firm)
......@@ -1567,11 +1538,11 @@ def test_joins_with_namespaced_model_should_use_correct_type
firm = Namespaced::Firm.create({ :name => 'Some Company' })
firm.clients.create({ :name => 'Some Client' })
stats = Namespaced::Firm.find(firm.id, {
stats = Namespaced::Firm.scoped(
:select => "#{Namespaced::Firm.table_name}.id, COUNT(#{Namespaced::Client.table_name}.id) AS num_clients",
:joins => :clients,
:group => "#{Namespaced::Firm.table_name}.id"
})
).find firm.id
assert_equal 1, stats.num_clients.to_i
ensure
......@@ -1580,7 +1551,7 @@ def test_joins_with_namespaced_model_should_use_correct_type
def test_association_proxy_transaction_method_starts_transaction_in_association_class
Comment.expects(:transaction)
Post.find(:first).comments.transaction do
Post.first.comments.transaction do
# nothing
end
end
......@@ -1597,7 +1568,7 @@ def test_respond_to_private_class_methods
end
def test_creating_using_primary_key
firm = Firm.find(:first, :order => "id")
firm = Firm.scoped(:order => "id").first
client = firm.clients_using_primary_key.create!(:name => 'test')
assert_equal firm.name, client.firm_name
end
......
......@@ -533,7 +533,7 @@ def test_dynamic_find_should_respect_association_include
end
def test_count_with_include_should_alias_join_table
assert_equal 2, people(:michael).posts.count(:include => :readers)
assert_equal 2, people(:michael).posts.includes(:readers).count
end
def test_inner_join_with_quoted_table_name
......@@ -568,7 +568,7 @@ def test_get_ids_for_unloaded_associations_does_not_load_them
def test_association_proxy_transaction_method_starts_transaction_in_association_class
Tag.expects(:transaction)
Post.find(:first).tags.transaction do
Post.first.tags.transaction do
# nothing
end
end
......@@ -651,7 +651,7 @@ def test_collection_singular_ids_getter_with_string_primary_keys
def test_collection_singular_ids_setter
company = companies(:rails_core)
dev = Developer.find(:first)
dev = Developer.first
company.developer_ids = [dev.id]
assert_equal [dev], company.developers
......@@ -671,7 +671,7 @@ def test_collection_singular_ids_setter_with_string_primary_keys
def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set
company = companies(:rails_core)
ids = [Developer.find(:first).id, -9999]
ids = [Developer.first.id, -9999]
assert_raises(ActiveRecord::RecordNotFound) {company.developer_ids= ids}
end
......
......@@ -25,13 +25,13 @@ def test_has_one_cache_nils
assert_queries(1) { assert_nil firm.account }
assert_queries(0) { assert_nil firm.account }
firms = Firm.find(:all, :include => :account)
firms = Firm.scoped(:includes => :account).all
assert_queries(0) { firms.each(&:account) }
end
def test_with_select
assert_equal Firm.find(1).account_with_select.attributes.size, 2
assert_equal Firm.find(1, :include => :account_with_select).account_with_select.attributes.size, 2
assert_equal Firm.scoped(:includes => :account_with_select).find(1).account_with_select.attributes.size, 2
end
def test_finding_using_primary_key
......@@ -294,13 +294,13 @@ def test_dependence_with_missing_association
def test_dependence_with_missing_association_and_nullify
Account.destroy_all
firm = DependentFirm.find(:first)
firm = DependentFirm.first
assert_nil firm.account
firm.destroy
end
def test_finding_with_interpolated_condition
firm = Firm.find(:first)
firm = Firm.first
superior = firm.clients.create(:name => 'SuperiorCo')
superior.rating = 10
superior.save
......@@ -346,14 +346,14 @@ def test_save_of_record_with_loaded_has_one
assert_nothing_raised do
Firm.find(@firm.id).save!
Firm.find(@firm.id, :include => :account).save!
Firm.scoped(:includes => :account).find(@firm.id).save!
end
@firm.account.destroy
assert_nothing_raised do
Firm.find(@firm.id).save!
Firm.find(@firm.id, :include => :account).save!
Firm.scoped(:includes => :account).find(@firm.id).save!
end
end
......
......@@ -73,7 +73,7 @@ def test_has_one_through_polymorphic
def test_has_one_through_eager_loading
members = assert_queries(3) do #base table, through table, clubs table
Member.find(:all, :include => :club, :conditions => ["name = ?", "Groucho Marx"])
Member.scoped(:includes => :club, :where => ["name = ?", "Groucho Marx"]).all
end
assert_equal 1, members.size
assert_not_nil assert_no_queries {members[0].club}
......@@ -81,7 +81,7 @@ def test_has_one_through_eager_loading
def test_has_one_through_eager_loading_through_polymorphic
members = assert_queries(3) do #base table, through table, clubs table
Member.find(:all, :include => :sponsor_club, :conditions => ["name = ?", "Groucho Marx"])
Member.scoped(:includes => :sponsor_club, :where => ["name = ?", "Groucho Marx"]).all
end
assert_equal 1, members.size
assert_not_nil assert_no_queries {members[0].sponsor_club}
......@@ -89,14 +89,14 @@ def test_has_one_through_eager_loading_through_polymorphic
def test_has_one_through_with_conditions_eager_loading
# conditions on the through table
assert_equal clubs(:moustache_club), Member.find(@member.id, :include => :favourite_club).favourite_club
assert_equal clubs(:moustache_club), Member.scoped(:includes => :favourite_club).find(@member.id).favourite_club
memberships(:membership_of_favourite_club).update_column(:favourite, false)
assert_equal nil, Member.find(@member.id, :include => :favourite_club).reload.favourite_club
assert_equal nil, Member.scoped(:includes => :favourite_club).find(@member.id).reload.favourite_club
# conditions on the source table
assert_equal clubs(:moustache_club), Member.find(@member.id, :include => :hairy_club).hairy_club
assert_equal clubs(:moustache_club), Member.scoped(:includes => :hairy_club).find(@member.id).hairy_club
clubs(:moustache_club).update_column(:name, "Association of Clean-Shaven Persons")
assert_equal nil, Member.find(@member.id, :include => :hairy_club).reload.hairy_club
assert_equal nil, Member.scoped(:includes => :hairy_club).find(@member.id).reload.hairy_club
end
def test_has_one_through_polymorphic_with_source_type
......@@ -104,14 +104,14 @@ def test_has_one_through_polymorphic_with_source_type
end
def test_eager_has_one_through_polymorphic_with_source_type
clubs = Club.find(:all, :include => :sponsored_member, :conditions => ["name = ?","Moustache and Eyebrow Fancier Club"])
clubs = Club.scoped(:includes => :sponsored_member, :where => ["name = ?","Moustache and Eyebrow Fancier Club"]).all
# Only the eyebrow fanciers club has a sponsored_member
assert_not_nil assert_no_queries {clubs[0].sponsored_member}
end
def test_has_one_through_nonpreload_eagerloading
members = assert_queries(1) do
Member.find(:all, :include => :club, :conditions => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name') #force fallback
Member.scoped(:includes => :club, :where => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name').all #force fallback
end
assert_equal 1, members.size
assert_not_nil assert_no_queries {members[0].club}
......@@ -119,7 +119,7 @@ def test_has_one_through_nonpreload_eagerloading
def test_has_one_through_nonpreload_eager_loading_through_polymorphic
members = assert_queries(1) do
Member.find(:all, :include => :sponsor_club, :conditions => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name') #force fallback
Member.scoped(:includes => :sponsor_club, :where => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name').all #force fallback
end
assert_equal 1, members.size
assert_not_nil assert_no_queries {members[0].sponsor_club}
......@@ -128,7 +128,7 @@ def test_has_one_through_nonpreload_eager_loading_through_polymorphic
def test_has_one_through_nonpreload_eager_loading_through_polymorphic_with_more_than_one_through_record
Sponsor.new(:sponsor_club => clubs(:crazy_club), :sponsorable => members(:groucho)).save!
members = assert_queries(1) do
Member.find(:all, :include => :sponsor_club, :conditions => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name DESC') #force fallback
Member.scoped(:includes => :sponsor_club, :where => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name DESC').all #force fallback
end
assert_equal 1, members.size
assert_not_nil assert_no_queries { members[0].sponsor_club }
......@@ -197,7 +197,7 @@ def test_preloading_has_one_through_on_belongs_to
@member.member_detail = @member_detail
@member.organization = @organization
@member_details = assert_queries(3) do
MemberDetail.find(:all, :include => :member_type)
MemberDetail.scoped(:includes => :member_type).all
end
@new_detail = @member_details[0]
assert @new_detail.send(:association, :member_type).loaded?
......@@ -210,14 +210,14 @@ def test_save_of_record_with_loaded_has_one_through
assert_nothing_raised do
Club.find(@club.id).save!
Club.find(@club.id, :include => :sponsored_member).save!
Club.scoped(:includes => :sponsored_member).find(@club.id).save!
end
@club.sponsor.destroy
assert_nothing_raised do
Club.find(@club.id).save!
Club.find(@club.id, :include => :sponsored_member).save!
Club.scoped(:includes => :sponsored_member).find(@club.id).save!
end
end
......
......@@ -72,17 +72,17 @@ def test_find_with_implicit_inner_joins_does_not_set_associations
def test_count_honors_implicit_inner_joins
real_count = Author.scoped.to_a.sum{|a| a.posts.count }
assert_equal real_count, Author.count(:joins => :posts), "plain inner join count should match the number of referenced posts records"
assert_equal real_count, Author.joins(:posts).count, "plain inner join count should match the number of referenced posts records"
end
def test_calculate_honors_implicit_inner_joins
real_count = Author.scoped.to_a.sum{|a| a.posts.count }
assert_equal real_count, Author.calculate(:count, 'authors.id', :joins => :posts), "plain inner join count should match the number of referenced posts records"
assert_equal real_count, Author.joins(:posts).calculate(:count, 'authors.id'), "plain inner join count should match the number of referenced posts records"
end
def test_calculate_honors_implicit_inner_joins_and_distinct_and_conditions
real_count = Author.scoped.to_a.select {|a| a.posts.any? {|p| p.title =~ /^Welcome/} }.length
authors_with_welcoming_post_titles = Author.calculate(:count, 'authors.id', :joins => :posts, :distinct => true, :conditions => "posts.title like 'Welcome%'")
authors_with_welcoming_post_titles = Author.scoped(:joins => :posts, :where => "posts.title like 'Welcome%'").calculate(:count, 'authors.id', :distinct => true)
assert_equal real_count, authors_with_welcoming_post_titles, "inner join and conditions should have only returned authors posting titles starting with 'Welcome'"
end
......
......@@ -96,7 +96,7 @@ def test_parent_instance_should_be_shared_with_child_on_find
def test_parent_instance_should_be_shared_with_eager_loaded_child_on_find
m = Man.find(:first, :conditions => {:name => 'Gordon'}, :include => :face)
m = Man.scoped(:where => {:name => 'Gordon'}, :includes => :face).first
f = m.face
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
m.name = 'Bongo'
......@@ -104,7 +104,7 @@ def test_parent_instance_should_be_shared_with_eager_loaded_child_on_find
f.man.name = 'Mungo'
assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance"
m = Man.find(:first, :conditions => {:name => 'Gordon'}, :include => :face, :order => 'faces.id')
m = Man.scoped(:where => {:name => 'Gordon'}, :includes => :face, :order => 'faces.id').first
f = m.face
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
m.name = 'Bongo'
......@@ -114,7 +114,7 @@ def test_parent_instance_should_be_shared_with_eager_loaded_child_on_find
end
def test_parent_instance_should_be_shared_with_newly_built_child
m = Man.find(:first)
m = Man.first
f = m.build_face(:description => 'haunted')
assert_not_nil f.man
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
......@@ -125,7 +125,7 @@ def test_parent_instance_should_be_shared_with_newly_built_child
end
def test_parent_instance_should_be_shared_with_newly_created_child
m = Man.find(:first)
m = Man.first
f = m.create_face(:description => 'haunted')
assert_not_nil f.man
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
......@@ -136,7 +136,7 @@ def test_parent_instance_should_be_shared_with_newly_created_child
end
def test_parent_instance_should_be_shared_with_newly_created_child_via_bang_method
m = Man.find(:first)
m = Man.first
f = m.create_face!(:description => 'haunted')
assert_not_nil f.man
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
......@@ -147,7 +147,7 @@ def test_parent_instance_should_be_shared_with_newly_created_child_via_bang_meth
end
def test_parent_instance_should_be_shared_with_replaced_via_accessor_child
m = Man.find(:first)
m = Man.first
f = Face.new(:description => 'haunted')
m.face = f
assert_not_nil f.man
......@@ -159,7 +159,7 @@ def test_parent_instance_should_be_shared_with_replaced_via_accessor_child
end
def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Man.find(:first).dirty_face }
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Man.first.dirty_face }
end
end
......@@ -179,7 +179,7 @@ def test_parent_instance_should_be_shared_with_every_child_on_find
end
def test_parent_instance_should_be_shared_with_eager_loaded_children
m = Man.find(:first, :conditions => {:name => 'Gordon'}, :include => :interests)
m = Man.scoped(:where => {:name => 'Gordon'}, :includes => :interests).first
is = m.interests
is.each do |i|
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
......@@ -189,7 +189,7 @@ def test_parent_instance_should_be_shared_with_eager_loaded_children
assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance"
end
m = Man.find(:first, :conditions => {:name => 'Gordon'}, :include => :interests, :order => 'interests.id')
m = Man.scoped(:where => {:name => 'Gordon'}, :includes => :interests, :order => 'interests.id').first
is = m.interests
is.each do |i|
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
......@@ -201,7 +201,7 @@ def test_parent_instance_should_be_shared_with_eager_loaded_children
end
def test_parent_instance_should_be_shared_with_newly_block_style_built_child
m = Man.find(:first)
m = Man.first
i = m.interests.build {|ii| ii.topic = 'Industrial Revolution Re-enactment'}
assert_not_nil i.topic, "Child attributes supplied to build via blocks should be populated"
assert_not_nil i.man
......@@ -213,7 +213,7 @@ def test_parent_instance_should_be_shared_with_newly_block_style_built_child
end
def test_parent_instance_should_be_shared_with_newly_created_via_bang_method_child
m = Man.find(:first)
m = Man.first
i = m.interests.create!(:topic => 'Industrial Revolution Re-enactment')
assert_not_nil i.man
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
......@@ -224,7 +224,7 @@ def test_parent_instance_should_be_shared_with_newly_created_via_bang_method_chi
end
def test_parent_instance_should_be_shared_with_newly_block_style_created_child
m = Man.find(:first)
m = Man.first
i = m.interests.create {|ii| ii.topic = 'Industrial Revolution Re-enactment'}
assert_not_nil i.topic, "Child attributes supplied to create via blocks should be populated"
assert_not_nil i.man
......@@ -248,7 +248,7 @@ def test_parent_instance_should_be_shared_with_poked_in_child
end
def test_parent_instance_should_be_shared_with_replaced_via_accessor_children
m = Man.find(:first)
m = Man.first
i = Interest.new(:topic => 'Industrial Revolution Re-enactment')
m.interests = [i]
assert_not_nil i.man
......@@ -260,7 +260,7 @@ def test_parent_instance_should_be_shared_with_replaced_via_accessor_children
end
def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Man.find(:first).secret_interests }
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Man.first.secret_interests }
end
end
......@@ -278,7 +278,7 @@ def test_child_instance_should_be_shared_with_parent_on_find
end
def test_eager_loaded_child_instance_should_be_shared_with_parent_on_find
f = Face.find(:first, :include => :man, :conditions => {:description => 'trusting'})
f = Face.scoped(:includes => :man, :where => {:description => 'trusting'}).first
m = f.man
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
f.description = 'gormless'
......@@ -286,7 +286,7 @@ def test_eager_loaded_child_instance_should_be_shared_with_parent_on_find
m.face.description = 'pleasing'
assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance"
f = Face.find(:first, :include => :man, :order => 'men.id', :conditions => {:description => 'trusting'})
f = Face.scoped(:includes => :man, :order => 'men.id', :where => {:description => 'trusting'}).first
m = f.man
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
f.description = 'gormless'
......@@ -331,7 +331,7 @@ def test_should_not_try_to_set_inverse_instances_when_the_inverse_is_a_has_many
end
def test_child_instance_should_be_shared_with_replaced_via_accessor_parent
f = Face.find(:first)
f = Face.first
m = Man.new(:name => 'Charles')
f.man = m
assert_not_nil m.face
......@@ -343,7 +343,7 @@ def test_child_instance_should_be_shared_with_replaced_via_accessor_parent
end
def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Face.find(:first).horrible_man }
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Face.first.horrible_man }
end
end
......@@ -351,7 +351,7 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase
fixtures :men, :faces, :interests
def test_child_instance_should_be_shared_with_parent_on_find
f = Face.find(:first, :conditions => {:description => 'confused'})
f = Face.scoped(:where => {:description => 'confused'}).first
m = f.polymorphic_man
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same before changes to child instance"
f.description = 'gormless'
......@@ -361,7 +361,7 @@ def test_child_instance_should_be_shared_with_parent_on_find
end
def test_eager_loaded_child_instance_should_be_shared_with_parent_on_find
f = Face.find(:first, :conditions => {:description => 'confused'}, :include => :man)
f = Face.scoped(:where => {:description => 'confused'}, :includes => :man).first
m = f.polymorphic_man
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same before changes to child instance"
f.description = 'gormless'
......@@ -369,7 +369,7 @@ def test_eager_loaded_child_instance_should_be_shared_with_parent_on_find
m.polymorphic_face.description = 'pleasing'
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to parent-owned instance"
f = Face.find(:first, :conditions => {:description => 'confused'}, :include => :man, :order => 'men.id')
f = Face.scoped(:where => {:description => 'confused'}, :includes => :man, :order => 'men.id').first
m = f.polymorphic_man
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same before changes to child instance"
f.description = 'gormless'
......@@ -421,19 +421,19 @@ def test_should_not_try_to_set_inverse_instances_when_the_inverse_is_a_has_many
def test_trying_to_access_inverses_that_dont_exist_shouldnt_raise_an_error
# Ideally this would, if only for symmetry's sake with other association types
assert_nothing_raised(ActiveRecord::InverseOfAssociationNotFoundError) { Face.find(:first).horrible_polymorphic_man }
assert_nothing_raised(ActiveRecord::InverseOfAssociationNotFoundError) { Face.first.horrible_polymorphic_man }
end
def test_trying_to_set_polymorphic_inverses_that_dont_exist_at_all_should_raise_an_error
# fails because no class has the correct inverse_of for horrible_polymorphic_man
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Face.find(:first).horrible_polymorphic_man = Man.first }
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Face.first.horrible_polymorphic_man = Man.first }
end
def test_trying_to_set_polymorphic_inverses_that_dont_exist_on_the_instance_being_set_should_raise_an_error
# passes because Man does have the correct inverse_of
assert_nothing_raised(ActiveRecord::InverseOfAssociationNotFoundError) { Face.find(:first).polymorphic_man = Man.first }
assert_nothing_raised(ActiveRecord::InverseOfAssociationNotFoundError) { Face.first.polymorphic_man = Man.first }
# fails because Interest does have the correct inverse_of
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Face.find(:first).polymorphic_man = Interest.first }
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Face.first.polymorphic_man = Interest.first }
end
end
......@@ -444,7 +444,7 @@ class InverseMultipleHasManyInversesForSameModel < ActiveRecord::TestCase
def test_that_we_can_load_associations_that_have_the_same_reciprocal_name_from_different_models
assert_nothing_raised(ActiveRecord::AssociationTypeMismatch) do
i = Interest.find(:first)
i = Interest.first
i.zine
i.man
end
......@@ -452,7 +452,7 @@ def test_that_we_can_load_associations_that_have_the_same_reciprocal_name_from_d
def test_that_we_can_create_associations_that_have_the_same_reciprocal_name_from_different_models
assert_nothing_raised(ActiveRecord::AssociationTypeMismatch) do
i = Interest.find(:first)
i = Interest.first
i.build_zine(:title => 'Get Some in Winter! 2008')
i.build_man(:name => 'Gordon')
i.save!
......
......@@ -46,12 +46,12 @@ def test_has_many_uniq_through_count
assert !authors(:mary).unique_categorized_posts.loaded?
assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count }
assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count(:title) }
assert_queries(1) { assert_equal 0, author.unique_categorized_posts.count(:title, :conditions => "title is NULL") }
assert_queries(1) { assert_equal 0, author.unique_categorized_posts.where(title: nil).count(:title) }
assert !authors(:mary).unique_categorized_posts.loaded?
end
def test_has_many_uniq_through_find
assert_equal 1, authors(:mary).unique_categorized_posts.find(:all).size
assert_equal 1, authors(:mary).unique_categorized_posts.all.size
end
def test_has_many_uniq_through_dynamic_find
......@@ -71,7 +71,7 @@ def test_count_polymorphic_has_many
end
def test_polymorphic_has_many_going_through_join_model_with_find
assert_equal tags(:general), tag = posts(:welcome).tags.find(:first)
assert_equal tags(:general), tag = posts(:welcome).tags.first
assert_no_queries do
tag.tagging
end
......@@ -85,7 +85,7 @@ def test_polymorphic_has_many_going_through_join_model_with_include_on_source_re
end
def test_polymorphic_has_many_going_through_join_model_with_include_on_source_reflection_with_find
assert_equal tags(:general), tag = posts(:welcome).funky_tags.find(:first)
assert_equal tags(:general), tag = posts(:welcome).funky_tags.first
assert_no_queries do
tag.tagging
end
......@@ -237,8 +237,8 @@ def test_has_many_with_piggyback
end
def test_include_has_many_through
posts = Post.find(:all, :order => 'posts.id')
posts_with_authors = Post.find(:all, :include => :authors, :order => 'posts.id')
posts = Post.scoped(:order => 'posts.id').all
posts_with_authors = Post.scoped(:includes => :authors, :order => 'posts.id').all
assert_equal posts.length, posts_with_authors.length
posts.length.times do |i|
assert_equal posts[i].authors.length, assert_no_queries { posts_with_authors[i].authors.length }
......@@ -246,7 +246,7 @@ def test_include_has_many_through
end
def test_include_polymorphic_has_one
post = Post.find_by_id(posts(:welcome).id, :include => :tagging)
post = Post.includes(:tagging).find posts(:welcome).id
tagging = taggings(:welcome_general)
assert_no_queries do
assert_equal tagging, post.tagging
......@@ -254,7 +254,7 @@ def test_include_polymorphic_has_one
end
def test_include_polymorphic_has_one_defined_in_abstract_parent
item = Item.find_by_id(items(:dvd).id, :include => :tagging)
item = Item.includes(:tagging).find items(:dvd).id
tagging = taggings(:godfather)
assert_no_queries do
assert_equal tagging, item.tagging
......@@ -262,8 +262,8 @@ def test_include_polymorphic_has_one_defined_in_abstract_parent
end
def test_include_polymorphic_has_many_through
posts = Post.find(:all, :order => 'posts.id')
posts_with_tags = Post.find(:all, :include => :tags, :order => 'posts.id')
posts = Post.scoped(:order => 'posts.id').all
posts_with_tags = Post.scoped(:includes => :tags, :order => 'posts.id').all
assert_equal posts.length, posts_with_tags.length
posts.length.times do |i|
assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length }
......@@ -271,8 +271,8 @@ def test_include_polymorphic_has_many_through
end
def test_include_polymorphic_has_many
posts = Post.find(:all, :order => 'posts.id')
posts_with_taggings = Post.find(:all, :include => :taggings, :order => 'posts.id')
posts = Post.scoped(:order => 'posts.id').all
posts_with_taggings = Post.scoped(:includes => :taggings, :order => 'posts.id').all
assert_equal posts.length, posts_with_taggings.length
posts.length.times do |i|
assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length }
......@@ -280,20 +280,20 @@ def test_include_polymorphic_has_many
end
def test_has_many_find_all
assert_equal [categories(:general)], authors(:david).categories.find(:all)
assert_equal [categories(:general)], authors(:david).categories.all
end
def test_has_many_find_first
assert_equal categories(:general), authors(:david).categories.find(:first)
assert_equal categories(:general), authors(:david).categories.first
end
def test_has_many_with_hash_conditions
assert_equal categories(:general), authors(:david).categories_like_general.find(:first)
assert_equal categories(:general), authors(:david).categories_like_general.first
end
def test_has_many_find_conditions
assert_equal categories(:general), authors(:david).categories.find(:first, :conditions => "categories.name = 'General'")
assert_nil authors(:david).categories.find(:first, :conditions => "categories.name = 'Technology'")
assert_equal categories(:general), authors(:david).categories.scoped(:where => "categories.name = 'General'").first
assert_nil authors(:david).categories.scoped(:where => "categories.name = 'Technology'").first
end
def test_has_many_class_methods_called_by_method_missing
......@@ -327,14 +327,6 @@ def test_has_many_through_with_custom_primary_key_on_has_many_source
assert_equal [authors(:david), authors(:bob)], posts(:thinking).authors_using_custom_pk.order('authors.id')
end
def test_both_scoped_and_explicit_joins_should_be_respected
assert_nothing_raised do
Post.send(:with_scope, :find => {:joins => "left outer join comments on comments.id = posts.id"}) do
Post.find :all, :select => "comments.id, authors.id", :joins => "left outer join authors on authors.id = posts.author_id"
end
end
end
def test_belongs_to_polymorphic_with_counter_cache
assert_equal 1, posts(:welcome)[:taggings_count]
tagging = posts(:welcome).taggings.create(:tag => tags(:general))
......@@ -372,7 +364,7 @@ def test_has_many_polymorphic_with_source_type
end
def test_eager_has_many_polymorphic_with_source_type
tag_with_include = Tag.find(tags(:general).id, :include => :tagged_posts)
tag_with_include = Tag.scoped(:includes => :tagged_posts).find(tags(:general).id)
desired = posts(:welcome, :thinking)
assert_no_queries do
# added sort by ID as otherwise test using JRuby was failing as array elements were in different order
......@@ -382,20 +374,20 @@ def test_eager_has_many_polymorphic_with_source_type
end
def test_has_many_through_has_many_find_all
assert_equal comments(:greetings), authors(:david).comments.find(:all, :order => 'comments.id').first
assert_equal comments(:greetings), authors(:david).comments.scoped(:order => 'comments.id').all.first
end
def test_has_many_through_has_many_find_all_with_custom_class
assert_equal comments(:greetings), authors(:david).funky_comments.find(:all, :order => 'comments.id').first
assert_equal comments(:greetings), authors(:david).funky_comments.scoped(:order => 'comments.id').all.first
end
def test_has_many_through_has_many_find_first
assert_equal comments(:greetings), authors(:david).comments.find(:first, :order => 'comments.id')
assert_equal comments(:greetings), authors(:david).comments.scoped(:order => 'comments.id').first
end
def test_has_many_through_has_many_find_conditions
options = { :conditions => "comments.#{QUOTED_TYPE}='SpecialComment'", :order => 'comments.id' }
assert_equal comments(:does_it_hurt), authors(:david).comments.find(:first, options)
options = { :where => "comments.#{QUOTED_TYPE}='SpecialComment'", :order => 'comments.id' }
assert_equal comments(:does_it_hurt), authors(:david).comments.scoped(options).first
end
def test_has_many_through_has_many_find_by_id
......@@ -411,7 +403,7 @@ def test_has_many_through_polymorphic_has_many
end
def test_include_has_many_through_polymorphic_has_many
author = Author.find_by_id(authors(:david).id, :include => :taggings)
author = Author.includes(:taggings).find authors(:david).id
expected_taggings = taggings(:welcome_general, :thinking_general)
assert_no_queries do
assert_equal expected_taggings, author.taggings.uniq.sort_by { |t| t.id }
......@@ -419,7 +411,7 @@ def test_include_has_many_through_polymorphic_has_many
end
def test_eager_load_has_many_through_has_many
author = Author.find :first, :conditions => ['name = ?', 'David'], :include => :comments, :order => 'comments.id'
author = Author.scoped(:where => ['name = ?', 'David'], :includes => :comments, :order => 'comments.id').first
SpecialComment.new; VerySpecialComment.new
assert_no_queries do
assert_equal [1,2,3,5,6,7,8,9,10,12], author.comments.collect(&:id)
......@@ -427,7 +419,7 @@ def test_eager_load_has_many_through_has_many
end
def test_eager_load_has_many_through_has_many_with_conditions
post = Post.find(:first, :include => :invalid_tags)
post = Post.scoped(:includes => :invalid_tags).first
assert_no_queries do
post.invalid_tags
end
......@@ -435,8 +427,8 @@ def test_eager_load_has_many_through_has_many_with_conditions
def test_eager_belongs_to_and_has_one_not_singularized
assert_nothing_raised do
Author.find(:first, :include => :author_address)
AuthorAddress.find(:first, :include => :author)
Author.scoped(:includes => :author_address).first
AuthorAddress.scoped(:includes => :author).first
end
end
......@@ -452,7 +444,7 @@ def test_add_to_self_referential_has_many_through
end
def test_has_many_through_uses_conditions_specified_on_the_has_many_association
author = Author.find(:first)
author = Author.first
assert_present author.comments
assert_blank author.nonexistant_comments
end
......@@ -622,7 +614,7 @@ def test_uniq_has_many_through_should_retain_order
def test_polymorphic_has_many
expected = taggings(:welcome_general)
p = Post.find(posts(:welcome).id, :include => :taggings)
p = Post.scoped(:includes => :taggings).find(posts(:welcome).id)
assert_no_queries {assert p.taggings.include?(expected)}
assert posts(:welcome).taggings.include?(taggings(:welcome_general))
end
......@@ -630,18 +622,18 @@ def test_polymorphic_has_many
def test_polymorphic_has_one
expected = posts(:welcome)
tagging = Tagging.find(taggings(:welcome_general).id, :include => :taggable)
tagging = Tagging.scoped(:includes => :taggable).find(taggings(:welcome_general).id)
assert_no_queries { assert_equal expected, tagging.taggable}
end
def test_polymorphic_belongs_to
p = Post.find(posts(:welcome).id, :include => {:taggings => :taggable})
p = Post.scoped(:includes => {:taggings => :taggable}).find(posts(:welcome).id)
assert_no_queries {assert_equal posts(:welcome), p.taggings.first.taggable}
end
def test_preload_polymorphic_has_many_through
posts = Post.find(:all, :order => 'posts.id')
posts_with_tags = Post.find(:all, :include => :tags, :order => 'posts.id')
posts = Post.scoped(:order => 'posts.id').all
posts_with_tags = Post.scoped(:includes => :tags, :order => 'posts.id').all
assert_equal posts.length, posts_with_tags.length
posts.length.times do |i|
assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length }
......@@ -649,7 +641,7 @@ def test_preload_polymorphic_has_many_through
end
def test_preload_polymorph_many_types
taggings = Tagging.find :all, :include => :taggable, :conditions => ['taggable_type != ?', 'FakeModel']
taggings = Tagging.scoped(:includes => :taggable, :where => ['taggable_type != ?', 'FakeModel']).all
assert_no_queries do
taggings.first.taggable.id
taggings[1].taggable.id
......@@ -662,13 +654,13 @@ def test_preload_polymorph_many_types
def test_preload_nil_polymorphic_belongs_to
assert_nothing_raised do
Tagging.find(:all, :include => :taggable, :conditions => ['taggable_type IS NULL'])
Tagging.scoped(:includes => :taggable, :where => ['taggable_type IS NULL']).all
end
end
def test_preload_polymorphic_has_many
posts = Post.find(:all, :order => 'posts.id')
posts_with_taggings = Post.find(:all, :include => :taggings, :order => 'posts.id')
posts = Post.scoped(:order => 'posts.id').all
posts_with_taggings = Post.scoped(:includes => :taggings, :order => 'posts.id').all
assert_equal posts.length, posts_with_taggings.length
posts.length.times do |i|
assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length }
......@@ -676,7 +668,7 @@ def test_preload_polymorphic_has_many
end
def test_belongs_to_shared_parent
comments = Comment.find(:all, :include => :post, :conditions => 'post_id = 1')
comments = Comment.scoped(:includes => :post, :where => 'post_id = 1').all
assert_no_queries do
assert_equal comments.first.post, comments[1].post
end
......
......@@ -74,8 +74,8 @@ def test_loading_the_association_target_should_load_most_recent_attributes_for_c
def test_include_with_order_works
assert_nothing_raised {Account.find(:first, :order => 'id', :include => :firm)}
assert_nothing_raised {Account.find(:first, :order => :id, :include => :firm)}
assert_nothing_raised {Account.scoped(:order => 'id', :includes => :firm).first}
assert_nothing_raised {Account.scoped(:order => :id, :includes => :firm).first}
end
def test_bad_collection_keys
......@@ -214,6 +214,10 @@ def test_proxy_association_accessor
david = developers(:david)
assert_equal david.association(:projects), david.projects.proxy_association
end
def test_scoped_allows_conditions
assert developers(:david).projects.scoped(where: 'foo').where_values.include?('foo')
end
end
class OverridingAssociationsTest < ActiveRecord::TestCase
......
......@@ -244,7 +244,7 @@ def test_case_sensitive_attributes_hash
# DB2 is not case-sensitive
return true if current_adapter?(:DB2Adapter)
assert_equal @loaded_fixtures['computers']['workstation'].to_hash, Computer.find(:first).attributes
assert_equal @loaded_fixtures['computers']['workstation'].to_hash, Computer.first.attributes
end
def test_hashes_not_mangled
......@@ -484,9 +484,9 @@ def test_typecast_attribute_from_select_to_false
topic = Topic.create(:title => 'Budget')
# Oracle does not support boolean expressions in SELECT
if current_adapter?(:OracleAdapter)
topic = Topic.find(:first, :select => "topics.*, 0 as is_test")
topic = Topic.scoped(:select => "topics.*, 0 as is_test").first
else
topic = Topic.find(:first, :select => "topics.*, 1=2 as is_test")
topic = Topic.scoped(:select => "topics.*, 1=2 as is_test").first
end
assert !topic.is_test?
end
......@@ -495,9 +495,9 @@ def test_typecast_attribute_from_select_to_true
topic = Topic.create(:title => 'Budget')
# Oracle does not support boolean expressions in SELECT
if current_adapter?(:OracleAdapter)
topic = Topic.find(:first, :select => "topics.*, 1 as is_test")
topic = Topic.scoped(:select => "topics.*, 1 as is_test").first
else
topic = Topic.find(:first, :select => "topics.*, 2=2 as is_test")
topic = Topic.scoped(:select => "topics.*, 2=2 as is_test").first
end
assert topic.is_test?
end
......
......@@ -87,7 +87,7 @@ def test_should_save_parent_but_not_invalid_child
end
def test_save_fails_for_invalid_has_one
firm = Firm.find(:first)
firm = Firm.first
assert firm.valid?
firm.build_account
......@@ -99,7 +99,7 @@ def test_save_fails_for_invalid_has_one
end
def test_save_succeeds_for_invalid_has_one_with_validate_false
firm = Firm.find(:first)
firm = Firm.first
assert firm.valid?
firm.build_unvalidated_account
......@@ -155,20 +155,20 @@ def test_assignment_before_either_saved
end
def test_not_resaved_when_unchanged
firm = Firm.find(:first, :include => :account)
firm = Firm.scoped(:includes => :account).first
firm.name += '-changed'
assert_queries(1) { firm.save! }
firm = Firm.find(:first)
firm.account = Account.find(:first)
firm = Firm.first
firm.account = Account.first
assert_queries(Firm.partial_updates? ? 0 : 1) { firm.save! }
firm = Firm.find(:first).dup
firm.account = Account.find(:first)
firm = Firm.first.dup
firm.account = Account.first
assert_queries(2) { firm.save! }
firm = Firm.find(:first).dup
firm.account = Account.find(:first).dup
firm = Firm.first.dup
firm.account = Account.first.dup
assert_queries(2) { firm.save! }
end
......@@ -228,7 +228,7 @@ def test_save_succeeds_for_invalid_belongs_to_with_validate_false
end
def test_assignment_before_parent_saved
client = Client.find(:first)
client = Client.first
apple = Firm.new("name" => "Apple")
client.firm = apple
assert_equal apple, client.firm
......@@ -342,7 +342,7 @@ def test_store_association_with_a_polymorphic_relationship
end
def test_build_and_then_save_parent_should_not_reload_target
client = Client.find(:first)
client = Client.first
apple = client.build_firm(:name => "Apple")
client.save!
assert_no_queries { assert_equal apple, client.firm }
......@@ -384,7 +384,7 @@ def test_invalid_adding_before_save
end
def test_invalid_adding_with_validate_false
firm = Firm.find(:first)
firm = Firm.first
client = Client.new
firm.unvalidated_clients_of_firm << client
......@@ -397,7 +397,7 @@ def test_invalid_adding_with_validate_false
def test_valid_adding_with_validate_false
no_of_clients = Client.count
firm = Firm.find(:first)
firm = Firm.first
client = Client.new("name" => "Apple")
assert firm.valid?
......
......@@ -192,7 +192,7 @@ def test_preserving_date_objects
end
def test_previously_changed
topic = Topic.find :first
topic = Topic.first
topic.title = '<3<3<3'
assert_equal({}, topic.previous_changes)
......@@ -202,7 +202,7 @@ def test_previously_changed
end
def test_previously_changed_dup
topic = Topic.find :first
topic = Topic.first
topic.title = '<3<3<3'
topic.save!
......@@ -228,7 +228,7 @@ def test_preserving_time_objects
)
# For adapters which support microsecond resolution.
if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:SQLiteAdapter)
if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:SQLite3Adapter)
assert_equal 11, Topic.find(1).written_on.sec
assert_equal 223300, Topic.find(1).written_on.usec
assert_equal 9900, Topic.find(2).written_on.usec
......@@ -350,13 +350,13 @@ def test_first_or_initialize
end
def test_load
topics = Topic.find(:all, :order => 'id')
topics = Topic.scoped(:order => 'id').all
assert_equal(4, topics.size)
assert_equal(topics(:first).title, topics.first.title)
end
def test_load_with_condition
topics = Topic.find(:all, :conditions => "author_name = 'Mary'")
topics = Topic.scoped(:where => "author_name = 'Mary'").all
assert_equal(1, topics.size)
assert_equal(topics(:second).title, topics.first.title)
......@@ -1264,10 +1264,10 @@ def test_column_name_properly_quoted
end
def test_quoting_arrays
replies = Reply.find(:all, :conditions => [ "id IN (?)", topics(:first).replies.collect(&:id) ])
replies = Reply.scoped(:where => [ "id IN (?)", topics(:first).replies.collect(&:id) ]).all
assert_equal topics(:first).replies.size, replies.size
replies = Reply.find(:all, :conditions => [ "id IN (?)", [] ])
replies = Reply.scoped(:where => [ "id IN (?)", [] ]).all
assert_equal 0, replies.size
end
......@@ -1568,22 +1568,19 @@ def test_sequence_name_with_abstract_class
def test_count_with_join
res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
res2 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'", :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
res2 = Post.where("posts.#{QUOTED_TYPE} = 'Post'").joins("LEFT JOIN comments ON posts.id=comments.post_id").count
assert_equal res, res2
res3 = nil
assert_nothing_raised do
res3 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'",
:joins => "LEFT JOIN comments ON posts.id=comments.post_id")
res3 = Post.where("posts.#{QUOTED_TYPE} = 'Post'").joins("LEFT JOIN comments ON posts.id=comments.post_id").count
end
assert_equal res, res3
res4 = Post.count_by_sql "SELECT COUNT(p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
res5 = nil
assert_nothing_raised do
res5 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
:joins => "p, comments co",
:select => "p.id")
res5 = Post.where("p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id").joins("p, comments co").select("p.id").count
end
assert_equal res4, res5
......@@ -1591,145 +1588,64 @@ def test_count_with_join
res6 = Post.count_by_sql "SELECT COUNT(DISTINCT p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
res7 = nil
assert_nothing_raised do
res7 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
:joins => "p, comments co",
:select => "p.id",
:distinct => true)
res7 = Post.where("p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id").joins("p, comments co").select("p.id").count(distinct: true)
end
assert_equal res6, res7
end
def test_scoped_find_conditions
scoped_developers = Developer.send(:with_scope, :find => { :conditions => 'salary > 90000' }) do
Developer.find(:all, :conditions => 'id < 5')
end
assert !scoped_developers.include?(developers(:david)) # David's salary is less than 90,000
assert_equal 3, scoped_developers.size
end
def test_no_limit_offset
assert_nothing_raised do
Developer.find(:all, :offset => 2)
end
end
def test_scoped_find_limit_offset
scoped_developers = Developer.send(:with_scope, :find => { :limit => 3, :offset => 2 }) do
Developer.find(:all, :order => 'id')
Developer.scoped(:offset => 2).all
end
assert !scoped_developers.include?(developers(:david))
assert !scoped_developers.include?(developers(:jamis))
assert_equal 3, scoped_developers.size
# Test without scoped find conditions to ensure we get the whole thing
developers = Developer.find(:all, :order => 'id')
assert_equal Developer.count, developers.size
end
def test_scoped_find_order
# Test order in scope
scoped_developers = Developer.send(:with_scope, :find => { :limit => 1, :order => 'salary DESC' }) do
Developer.find(:all)
end
assert_equal 'Jamis', scoped_developers.first.name
assert scoped_developers.include?(developers(:jamis))
# Test scope without order and order in find
scoped_developers = Developer.send(:with_scope, :find => { :limit => 1 }) do
Developer.find(:all, :order => 'salary DESC')
end
# Test scope order + find order, order has priority
scoped_developers = Developer.send(:with_scope, :find => { :limit => 3, :order => 'id DESC' }) do
Developer.find(:all, :order => 'salary ASC')
end
assert scoped_developers.include?(developers(:poor_jamis))
assert ! scoped_developers.include?(developers(:david))
assert ! scoped_developers.include?(developers(:jamis))
assert_equal 3, scoped_developers.size
# Test without scoped find conditions to ensure we get the right thing
assert ! scoped_developers.include?(Developer.find(1))
assert scoped_developers.include?(Developer.find(11))
end
def test_scoped_find_limit_offset_including_has_many_association
topics = Topic.send(:with_scope, :find => {:limit => 1, :offset => 1, :include => :replies}) do
Topic.find(:all, :order => "topics.id")
end
assert_equal 1, topics.size
assert_equal 2, topics.first.id
end
def test_scoped_find_order_including_has_many_association
developers = Developer.send(:with_scope, :find => { :order => 'developers.salary DESC', :include => :projects }) do
Developer.find(:all)
end
assert developers.size >= 2
(1...developers.size).each do |i|
assert developers[i-1].salary >= developers[i].salary
end
end
def test_scoped_find_with_group_and_having
developers = Developer.send(:with_scope, :find => { :group => 'developers.salary', :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" }) do
Developer.find(:all)
end
assert_equal 3, developers.size
end
def test_find_last
last = Developer.find :last
assert_equal last, Developer.find(:first, :order => 'id desc')
last = Developer.last
assert_equal last, Developer.scoped(:order => 'id desc').first
end
def test_last
assert_equal Developer.find(:first, :order => 'id desc'), Developer.last
assert_equal Developer.scoped(:order => 'id desc').first, Developer.last
end
def test_all
developers = Developer.all
assert_kind_of Array, developers
assert_equal Developer.find(:all), developers
assert_equal Developer.all, developers
end
def test_all_with_conditions
assert_equal Developer.find(:all, :order => 'id desc'), Developer.order('id desc').all
assert_equal Developer.scoped(:order => 'id desc').all, Developer.order('id desc').all
end
def test_find_ordered_last
last = Developer.find :last, :order => 'developers.salary ASC'
assert_equal last, Developer.find(:all, :order => 'developers.salary ASC').last
last = Developer.scoped(:order => 'developers.salary ASC').last
assert_equal last, Developer.scoped(:order => 'developers.salary ASC').all.last
end
def test_find_reverse_ordered_last
last = Developer.find :last, :order => 'developers.salary DESC'
assert_equal last, Developer.find(:all, :order => 'developers.salary DESC').last
last = Developer.scoped(:order => 'developers.salary DESC').last
assert_equal last, Developer.scoped(:order => 'developers.salary DESC').all.last
end
def test_find_multiple_ordered_last
last = Developer.find :last, :order => 'developers.name, developers.salary DESC'
assert_equal last, Developer.find(:all, :order => 'developers.name, developers.salary DESC').last
last = Developer.scoped(:order => 'developers.name, developers.salary DESC').last
assert_equal last, Developer.scoped(:order => 'developers.name, developers.salary DESC').all.last
end
def test_find_keeps_multiple_order_values
combined = Developer.find(:all, :order => 'developers.name, developers.salary')
assert_equal combined, Developer.find(:all, :order => ['developers.name', 'developers.salary'])
combined = Developer.scoped(:order => 'developers.name, developers.salary').all
assert_equal combined, Developer.scoped(:order => ['developers.name', 'developers.salary']).all
end
def test_find_keeps_multiple_group_values
combined = Developer.find(:all, :group => 'developers.name, developers.salary, developers.id, developers.created_at, developers.updated_at')
assert_equal combined, Developer.find(:all, :group => ['developers.name', 'developers.salary', 'developers.id', 'developers.created_at', 'developers.updated_at'])
combined = Developer.scoped(:group => 'developers.name, developers.salary, developers.id, developers.created_at, developers.updated_at').all
assert_equal combined, Developer.scoped(:group => ['developers.name', 'developers.salary', 'developers.id', 'developers.created_at', 'developers.updated_at']).all
end
def test_find_symbol_ordered_last
last = Developer.find :last, :order => :salary
assert_equal last, Developer.find(:all, :order => :salary).last
end
def test_find_scoped_ordered_last
last_developer = Developer.send(:with_scope, :find => { :order => 'developers.salary ASC' }) do
Developer.find(:last)
end
assert_equal last_developer, Developer.find(:all, :order => 'developers.salary ASC').last
last = Developer.scoped(:order => :salary).last
assert_equal last, Developer.scoped(:order => :salary).all.last
end
def test_abstract_class
......@@ -1804,7 +1720,7 @@ def test_assert_queries
end
def test_to_param_should_return_string
assert_kind_of String, Client.find(:first).to_param
assert_kind_of String, Client.first.to_param
end
def test_inspect_class
......@@ -1823,8 +1739,8 @@ def test_inspect_new_instance
end
def test_inspect_limited_select_instance
assert_equal %(#<Topic id: 1>), Topic.find(:first, :select => 'id', :conditions => 'id = 1').inspect
assert_equal %(#<Topic id: 1, title: "The First Topic">), Topic.find(:first, :select => 'id, title', :conditions => 'id = 1').inspect
assert_equal %(#<Topic id: 1>), Topic.scoped(:select => 'id', :where => 'id = 1').first.inspect
assert_equal %(#<Topic id: 1, title: "The First Topic">), Topic.scoped(:select => 'id, title', :where => 'id = 1').first.inspect
end
def test_inspect_class_without_table
......@@ -2087,4 +2003,9 @@ def test_slice
assert_equal record, klass.public_send(meth, :foo, :bar)
end
end
test "scoped can take a values hash" do
klass = Class.new(ActiveRecord::Base)
assert_equal ['foo'], klass.scoped(select: 'foo').select_values
end
end
......@@ -27,30 +27,18 @@ def test_each_should_not_return_query_chain_and_execcute_only_one_query
def test_each_should_raise_if_select_is_set_without_id
assert_raise(RuntimeError) do
Post.find_each(:select => :title, :batch_size => 1) { |post| post }
Post.select(:title).find_each(:batch_size => 1) { |post| post }
end
end
def test_each_should_execute_if_id_is_in_select
assert_queries(6) do
Post.find_each(:select => "id, title, type", :batch_size => 2) do |post|
Post.select("id, title, type").find_each(:batch_size => 2) do |post|
assert_kind_of Post, post
end
end
end
def test_each_should_raise_if_the_order_is_set
assert_raise(RuntimeError) do
Post.find_each(:order => "title") { |post| post }
end
end
def test_each_should_raise_if_the_limit_is_set
assert_raise(RuntimeError) do
Post.find_each(:limit => 1) { |post| post }
end
end
def test_warn_if_limit_scope_is_set
ActiveRecord::Base.logger.expects(:warn)
Post.limit(1).find_each { |post| post }
......
......@@ -9,7 +9,7 @@ def test_custom_lock
if current_adapter?(:MysqlAdapter, :Mysql2Adapter)
assert_match 'SHARE MODE', Person.lock('LOCK IN SHARE MODE').to_sql
assert_sql(/LOCK IN SHARE MODE/) do
Person.find(1, :lock => 'LOCK IN SHARE MODE')
Person.scoped(:lock => 'LOCK IN SHARE MODE').find(1)
end
end
end
......
require "cases/helper"
require 'models/entrant'
class DeprecatedFinderTest < ActiveRecord::TestCase
fixtures :entrants
def test_deprecated_find_all_was_removed
assert_raise(NoMethodError) { Entrant.find_all }
end
def test_deprecated_find_first_was_removed
assert_raise(NoMethodError) { Entrant.find_first }
end
def test_deprecated_find_on_conditions_was_removed
assert_raise(NoMethodError) { Entrant.find_on_conditions }
end
def test_count
assert_equal(0, Entrant.count(:conditions => "id > 3"))
assert_equal(1, Entrant.count(:conditions => ["id > ?", 2]))
assert_equal(2, Entrant.count(:conditions => ["id > ?", 1]))
end
def test_count_by_sql
assert_equal(0, Entrant.count_by_sql("SELECT COUNT(*) FROM entrants WHERE id > 3"))
assert_equal(1, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 2]))
assert_equal(2, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 1]))
end
end
......@@ -288,7 +288,7 @@ def test_partial_update
with_partial_updates Pirate, false do
assert_queries(2) { 2.times { pirate.save! } }
Pirate.update_all({ :updated_on => old_updated_on }, :id => pirate.id)
Pirate.where(id: pirate.id).update_all(:updated_on => old_updated_on)
end
with_partial_updates Pirate, true do
......@@ -306,7 +306,7 @@ def test_partial_update_with_optimistic_locking
with_partial_updates Person, false do
assert_queries(2) { 2.times { person.save! } }
Person.update_all({ :first_name => 'baz' }, :id => person.id)
Person.where(id: person.id).update_all(:first_name => 'baz')
end
with_partial_updates Person, true do
......
......@@ -716,7 +716,7 @@ def test_supports_polymorphic_belongs_to
end
def test_only_generates_a_pk_if_necessary
m = Matey.find(:first)
m = Matey.first
m.pirate = pirates(:blackbeard)
m.target = pirates(:redbeard)
end
......
......@@ -36,7 +36,7 @@ def current_adapter?(*types)
end
def in_memory_db?
current_adapter?(:SQLiteAdapter) &&
current_adapter?(:SQLite3Adapter) &&
ActiveRecord::Base.connection_pool.spec.config[:database] == ":memory:"
end
......
......@@ -15,7 +15,7 @@ def test_class_with_store_full_sti_class_returns_full_name
end
def test_class_with_blank_sti_name
company = Company.find(:first)
company = Company.first
company = company.dup
company.extend(Module.new {
def read_attribute(name)
......@@ -24,7 +24,7 @@ def read_attribute(name)
end
})
company.save!
company = Company.find(:all).find { |x| x.id == company.id }
company = Company.all.find { |x| x.id == company.id }
assert_equal ' ', company.type
end
......@@ -98,7 +98,7 @@ def test_alt_inheritance_find
end
def test_inheritance_find_all
companies = Company.find(:all, :order => 'id')
companies = Company.scoped(:order => 'id').all
assert_kind_of Firm, companies[0], "37signals should be a firm"
assert_kind_of Client, companies[1], "Summit should be a client"
end
......@@ -149,9 +149,9 @@ def test_alt_finding_incorrect_type_data
def test_update_all_within_inheritance
Client.update_all "name = 'I am a client'"
assert_equal "I am a client", Client.find(:all).first.name
assert_equal "I am a client", Client.all.first.name
# Order by added as otherwise Oracle tests were failing because of different order of results
assert_equal "37signals", Firm.find(:all, :order => "id").first.name
assert_equal "37signals", Firm.scoped(:order => "id").all.first.name
end
def test_alt_update_all_within_inheritance
......@@ -173,9 +173,9 @@ def test_alt_destroy_all_within_inheritance
end
def test_find_first_within_inheritance
assert_kind_of Firm, Company.find(:first, :conditions => "name = '37signals'")
assert_kind_of Firm, Firm.find(:first, :conditions => "name = '37signals'")
assert_nil Client.find(:first, :conditions => "name = '37signals'")
assert_kind_of Firm, Company.scoped(:where => "name = '37signals'").first
assert_kind_of Firm, Firm.scoped(:where => "name = '37signals'").first
assert_nil Client.scoped(:where => "name = '37signals'").first
end
def test_alt_find_first_within_inheritance
......@@ -187,10 +187,10 @@ def test_alt_find_first_within_inheritance
def test_complex_inheritance
very_special_client = VerySpecialClient.create("name" => "veryspecial")
assert_equal very_special_client, VerySpecialClient.where("name = 'veryspecial'").first
assert_equal very_special_client, SpecialClient.find(:first, :conditions => "name = 'veryspecial'")
assert_equal very_special_client, Company.find(:first, :conditions => "name = 'veryspecial'")
assert_equal very_special_client, Client.find(:first, :conditions => "name = 'veryspecial'")
assert_equal 1, Client.find(:all, :conditions => "name = 'Summit'").size
assert_equal very_special_client, SpecialClient.scoped(:where => "name = 'veryspecial'").first
assert_equal very_special_client, Company.scoped(:where => "name = 'veryspecial'").first
assert_equal very_special_client, Client.scoped(:where => "name = 'veryspecial'").first
assert_equal 1, Client.scoped(:where => "name = 'Summit'").all.size
assert_equal very_special_client, Client.find(very_special_client.id)
end
......@@ -201,14 +201,14 @@ def test_alt_complex_inheritance
end
def test_eager_load_belongs_to_something_inherited
account = Account.find(1, :include => :firm)
account = Account.scoped(:includes => :firm).find(1)
assert account.association_cache.key?(:firm), "nil proves eager load failed"
end
def test_eager_load_belongs_to_primary_key_quoting
con = Account.connection
assert_sql(/#{con.quote_table_name('companies')}.#{con.quote_column_name('id')} IN \(1\)/) do
Account.find(1, :include => :firm)
Account.scoped(:includes => :firm).find(1)
end
end
......@@ -230,7 +230,7 @@ def test_inheritance_without_mapping
private
def switch_to_alt_inheritance_column
# we don't want misleading test results, so get rid of the values in the type column
Company.find(:all, :order => 'id').each do |c|
Company.scoped(:order => 'id').all.each do |c|
c['type'] = nil
c.save
end
......@@ -259,7 +259,7 @@ def teardown
def test_instantiation_doesnt_try_to_require_corresponding_file
ActiveRecord::Base.store_full_sti_class = false
foo = Firm.find(:first).clone
foo = Firm.first.clone
foo.ruby_type = foo.type = 'FirmOnTheFly'
foo.save!
......
......@@ -63,7 +63,7 @@ def test_column_string_type_and_limit
end
def test_column_null_not_null
subscriber = Subscriber.find(:first)
subscriber = Subscriber.first
assert subscriber.column_for_attribute("name").null
assert !subscriber.column_for_attribute("nick").null
end
......
......@@ -86,7 +86,7 @@ def test_validates_associated_missing
assert !r.valid?
assert r.errors[:topic].any?
r.topic = Topic.find :first
r.topic = Topic.first
assert r.valid?
end
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册