activerecord 3.2.22.5 → 4.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1024 -543
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +20 -29
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +55 -44
  7. data/lib/active_record/aggregations.rb +40 -34
  8. data/lib/active_record/associations.rb +204 -276
  9. data/lib/active_record/associations/alias_tracker.rb +1 -1
  10. data/lib/active_record/associations/association.rb +30 -35
  11. data/lib/active_record/associations/association_scope.rb +40 -40
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -2
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +35 -57
  15. data/lib/active_record/associations/builder/collection_association.rb +54 -40
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -64
  18. data/lib/active_record/associations/builder/has_one.rb +13 -50
  19. data/lib/active_record/associations/builder/singular_association.rb +13 -13
  20. data/lib/active_record/associations/collection_association.rb +92 -88
  21. data/lib/active_record/associations/collection_proxy.rb +913 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
  23. data/lib/active_record/associations/has_many_association.rb +35 -9
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -14
  25. data/lib/active_record/associations/has_one_association.rb +33 -13
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +2 -2
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
  29. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  30. data/lib/active_record/associations/join_helper.rb +1 -11
  31. data/lib/active_record/associations/preloader.rb +14 -17
  32. data/lib/active_record/associations/preloader/association.rb +29 -33
  33. data/lib/active_record/associations/preloader/collection_association.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
  35. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  36. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +13 -17
  38. data/lib/active_record/associations/singular_association.rb +11 -11
  39. data/lib/active_record/associations/through_association.rb +2 -2
  40. data/lib/active_record/attribute_assignment.rb +133 -153
  41. data/lib/active_record/attribute_methods.rb +196 -93
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  43. data/lib/active_record/attribute_methods/dirty.rb +31 -28
  44. data/lib/active_record/attribute_methods/primary_key.rb +38 -30
  45. data/lib/active_record/attribute_methods/query.rb +5 -4
  46. data/lib/active_record/attribute_methods/read.rb +62 -91
  47. data/lib/active_record/attribute_methods/serialization.rb +97 -66
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
  49. data/lib/active_record/attribute_methods/write.rb +32 -39
  50. data/lib/active_record/autosave_association.rb +56 -70
  51. data/lib/active_record/base.rb +53 -450
  52. data/lib/active_record/callbacks.rb +53 -18
  53. data/lib/active_record/coders/yaml_column.rb +11 -9
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
  65. data/lib/active_record/connection_adapters/column.rb +46 -24
  66. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  67. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  68. data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
  69. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  70. data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
  73. data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
  74. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  75. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
  76. data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
  77. data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
  79. data/lib/active_record/connection_handling.rb +98 -0
  80. data/lib/active_record/core.rb +428 -0
  81. data/lib/active_record/counter_cache.rb +106 -108
  82. data/lib/active_record/dynamic_matchers.rb +110 -63
  83. data/lib/active_record/errors.rb +25 -8
  84. data/lib/active_record/explain.rb +8 -58
  85. data/lib/active_record/explain_subscriber.rb +6 -3
  86. data/lib/active_record/fixture_set/file.rb +56 -0
  87. data/lib/active_record/fixtures.rb +146 -148
  88. data/lib/active_record/inheritance.rb +77 -59
  89. data/lib/active_record/integration.rb +5 -5
  90. data/lib/active_record/locale/en.yml +8 -1
  91. data/lib/active_record/locking/optimistic.rb +38 -42
  92. data/lib/active_record/locking/pessimistic.rb +4 -4
  93. data/lib/active_record/log_subscriber.rb +19 -9
  94. data/lib/active_record/migration.rb +318 -153
  95. data/lib/active_record/migration/command_recorder.rb +90 -31
  96. data/lib/active_record/migration/join_table.rb +15 -0
  97. data/lib/active_record/model_schema.rb +69 -92
  98. data/lib/active_record/nested_attributes.rb +113 -148
  99. data/lib/active_record/null_relation.rb +65 -0
  100. data/lib/active_record/persistence.rb +188 -97
  101. data/lib/active_record/query_cache.rb +18 -36
  102. data/lib/active_record/querying.rb +19 -15
  103. data/lib/active_record/railtie.rb +91 -36
  104. data/lib/active_record/railties/console_sandbox.rb +0 -2
  105. data/lib/active_record/railties/controller_runtime.rb +2 -2
  106. data/lib/active_record/railties/databases.rake +90 -309
  107. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  108. data/lib/active_record/readonly_attributes.rb +7 -3
  109. data/lib/active_record/reflection.rb +72 -56
  110. data/lib/active_record/relation.rb +241 -157
  111. data/lib/active_record/relation/batches.rb +25 -22
  112. data/lib/active_record/relation/calculations.rb +143 -121
  113. data/lib/active_record/relation/delegation.rb +96 -18
  114. data/lib/active_record/relation/finder_methods.rb +117 -183
  115. data/lib/active_record/relation/merger.rb +133 -0
  116. data/lib/active_record/relation/predicate_builder.rb +90 -42
  117. data/lib/active_record/relation/query_methods.rb +666 -136
  118. data/lib/active_record/relation/spawn_methods.rb +43 -150
  119. data/lib/active_record/result.rb +33 -6
  120. data/lib/active_record/sanitization.rb +24 -50
  121. data/lib/active_record/schema.rb +19 -12
  122. data/lib/active_record/schema_dumper.rb +31 -39
  123. data/lib/active_record/schema_migration.rb +36 -0
  124. data/lib/active_record/scoping.rb +0 -124
  125. data/lib/active_record/scoping/default.rb +48 -45
  126. data/lib/active_record/scoping/named.rb +74 -103
  127. data/lib/active_record/serialization.rb +6 -2
  128. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  129. data/lib/active_record/store.rb +119 -15
  130. data/lib/active_record/tasks/database_tasks.rb +158 -0
  131. data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
  132. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  133. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  134. data/lib/active_record/test_case.rb +61 -38
  135. data/lib/active_record/timestamp.rb +8 -9
  136. data/lib/active_record/transactions.rb +65 -51
  137. data/lib/active_record/validations.rb +17 -15
  138. data/lib/active_record/validations/associated.rb +20 -14
  139. data/lib/active_record/validations/presence.rb +65 -0
  140. data/lib/active_record/validations/uniqueness.rb +93 -52
  141. data/lib/active_record/version.rb +4 -4
  142. data/lib/rails/generators/active_record.rb +3 -5
  143. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
  144. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  145. data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
  146. data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
  147. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  148. metadata +53 -46
  149. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  150. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  151. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  152. data/lib/active_record/dynamic_finder_match.rb +0 -68
  153. data/lib/active_record/dynamic_scope_match.rb +0 -23
  154. data/lib/active_record/fixtures/file.rb +0 -65
  155. data/lib/active_record/identity_map.rb +0 -162
  156. data/lib/active_record/observer.rb +0 -121
  157. data/lib/active_record/session_store.rb +0 -360
  158. data/lib/rails/generators/active_record/migration.rb +0 -15
  159. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  160. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  161. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  162. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,25 +1,106 @@
1
- require 'active_support/core_ext/module/delegation'
1
+ require 'thread'
2
+ require 'thread_safe'
2
3
 
3
4
  module ActiveRecord
4
- module Delegation
5
- # Set up common delegations for performance (avoids method_missing)
5
+ module Delegation # :nodoc:
6
+ extend ActiveSupport::Concern
7
+
8
+ # This module creates compiled delegation methods dynamically at runtime, which makes
9
+ # subsequent calls to that method faster by avoiding method_missing. The delegations
10
+ # may vary depending on the klass of a relation, so we create a subclass of Relation
11
+ # for each different klass, and the delegations are compiled into that subclass only.
12
+
6
13
  delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :to => :to_a
7
14
  delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
8
- :connection, :columns_hash, :auto_explain_threshold_in_seconds, :to => :klass
15
+ :connection, :columns_hash, :to => :klass
16
+
17
+ module ClassSpecificRelation
18
+ extend ActiveSupport::Concern
19
+
20
+ included do
21
+ @delegation_mutex = Mutex.new
22
+ end
23
+
24
+ module ClassMethods
25
+ def name
26
+ superclass.name
27
+ end
9
28
 
10
- def self.delegate_to_scoped_klass(method)
11
- if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/
12
- module_eval <<-RUBY, __FILE__, __LINE__ + 1
13
- def #{method}(*args, &block)
14
- scoping { @klass.#{method}(*args, &block) }
29
+ def delegate_to_scoped_klass(method)
30
+ @delegation_mutex.synchronize do
31
+ return if method_defined?(method)
32
+
33
+ if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/
34
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
35
+ def #{method}(*args, &block)
36
+ scoping { @klass.#{method}(*args, &block) }
37
+ end
38
+ RUBY
39
+ else
40
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
41
+ def #{method}(*args, &block)
42
+ scoping { @klass.send(#{method.inspect}, *args, &block) }
43
+ end
44
+ RUBY
45
+ end
15
46
  end
16
- RUBY
17
- else
18
- module_eval <<-RUBY, __FILE__, __LINE__ + 1
19
- def #{method}(*args, &block)
20
- scoping { @klass.send(#{method.inspect}, *args, &block) }
47
+ end
48
+
49
+ def delegate(method, opts = {})
50
+ @delegation_mutex.synchronize do
51
+ return if method_defined?(method)
52
+ super
53
+ end
54
+ end
55
+ end
56
+
57
+ protected
58
+
59
+ def method_missing(method, *args, &block)
60
+ if @klass.respond_to?(method)
61
+ self.class.delegate_to_scoped_klass(method)
62
+ scoping { @klass.send(method, *args, &block) }
63
+ elsif Array.method_defined?(method)
64
+ self.class.delegate method, :to => :to_a
65
+ to_a.send(method, *args, &block)
66
+ elsif arel.respond_to?(method)
67
+ self.class.delegate method, :to => :arel
68
+ arel.send(method, *args, &block)
69
+ else
70
+ super
71
+ end
72
+ end
73
+ end
74
+
75
+ module ClassMethods
76
+ @@subclasses = ThreadSafe::Cache.new(:initial_capacity => 2)
77
+
78
+ def new(klass, *args)
79
+ relation = relation_class_for(klass).allocate
80
+ relation.__send__(:initialize, klass, *args)
81
+ relation
82
+ end
83
+
84
+ # This doesn't have to be thread-safe. relation_class_for guarantees that this will only be
85
+ # called exactly once for a given const name.
86
+ def const_missing(name)
87
+ const_set(name, Class.new(self) { include ClassSpecificRelation })
88
+ end
89
+
90
+ private
91
+ # Cache the constants in @@subclasses because looking them up via const_get
92
+ # make instantiation significantly slower.
93
+ def relation_class_for(klass)
94
+ if klass && (klass_name = klass.name)
95
+ my_cache = @@subclasses.compute_if_absent(self) { ThreadSafe::Cache.new }
96
+ # This hash is keyed by klass.name to avoid memory leaks in development mode
97
+ my_cache.compute_if_absent(klass_name) do
98
+ # Cache#compute_if_absent guarantees that the block will only executed once for the given klass_name
99
+ const_get("#{name.gsub('::', '_')}_#{klass_name.gsub('::', '_')}", false)
21
100
  end
22
- RUBY
101
+ else
102
+ ActiveRecord::Relation
103
+ end
23
104
  end
24
105
  end
25
106
 
@@ -33,13 +114,10 @@ module ActiveRecord
33
114
 
34
115
  def method_missing(method, *args, &block)
35
116
  if @klass.respond_to?(method)
36
- ::ActiveRecord::Delegation.delegate_to_scoped_klass(method)
37
117
  scoping { @klass.send(method, *args, &block) }
38
118
  elsif Array.method_defined?(method)
39
- ::ActiveRecord::Delegation.delegate method, :to => :to_a
40
119
  to_a.send(method, *args, &block)
41
120
  elsif arel.respond_to?(method)
42
- ::ActiveRecord::Delegation.delegate method, :to => :arel
43
121
  arel.send(method, *args, &block)
44
122
  else
45
123
  super
@@ -1,85 +1,21 @@
1
- require 'active_support/core_ext/object/blank'
2
- require 'active_support/core_ext/hash/indifferent_access'
3
-
4
1
  module ActiveRecord
5
2
  module FinderMethods
6
- # Find operates with four different retrieval approaches:
7
- #
8
- # * Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
9
- # If no record can be found for all of the listed ids, then RecordNotFound will be raised.
10
- # * Find first - This will return the first record matched by the options used. These options can either be specific
11
- # conditions or merely an order. If no record can be matched, +nil+ is returned. Use
12
- # <tt>Model.find(:first, *args)</tt> or its shortcut <tt>Model.first(*args)</tt>.
13
- # * Find last - This will return the last record matched by the options used. These options can either be specific
14
- # conditions or merely an order. If no record can be matched, +nil+ is returned. Use
15
- # <tt>Model.find(:last, *args)</tt> or its shortcut <tt>Model.last(*args)</tt>.
16
- # * Find all - This will return all the records matched by the options used.
17
- # If no records are found, an empty array is returned. Use
18
- # <tt>Model.find(:all, *args)</tt> or its shortcut <tt>Model.all(*args)</tt>.
19
- #
20
- # All approaches accept an options hash as their last parameter.
21
- #
22
- # ==== Options
23
- #
24
- # * <tt>:conditions</tt> - An SQL fragment like "administrator = 1", <tt>["user_name = ?", username]</tt>,
25
- # or <tt>["user_name = :user_name", { :user_name => user_name }]</tt>. See conditions in the intro.
26
- # * <tt>:order</tt> - An SQL fragment like "created_at DESC, name".
27
- # * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
28
- # * <tt>:having</tt> - Combined with +:group+ this can be used to filter the records that a
29
- # <tt>GROUP BY</tt> returns. Uses the <tt>HAVING</tt> SQL-clause.
30
- # * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned.
31
- # * <tt>:offset</tt> - An integer determining the offset from where the rows should be fetched. So at 5,
32
- # it would skip rows 0 through 4.
33
- # * <tt>:joins</tt> - Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed),
34
- # named associations in the same form used for the <tt>:include</tt> option, which will perform an
35
- # <tt>INNER JOIN</tt> on the associated table(s),
36
- # or an array containing a mixture of both strings and named associations.
37
- # If the value is a string, then the records will be returned read-only since they will
38
- # have attributes that do not correspond to the table's columns.
39
- # Pass <tt>:readonly => false</tt> to override.
40
- # * <tt>:include</tt> - Names associations that should be loaded alongside. The symbols named refer
41
- # to already defined associations. See eager loading under Associations.
42
- # * <tt>:select</tt> - By default, this is "*" as in "SELECT * FROM", but can be changed if you,
43
- # for example, want to do a join but not include the joined columns. Takes a string with the SELECT SQL fragment (e.g. "id, name").
44
- # * <tt>:from</tt> - By default, this is the table name of the class, but can be changed
45
- # to an alternate table name (or even the name of a database view).
46
- # * <tt>:readonly</tt> - Mark the returned records read-only so they cannot be saved or updated.
47
- # * <tt>:lock</tt> - An SQL fragment like "FOR UPDATE" or "LOCK IN SHARE MODE".
48
- # <tt>:lock => true</tt> gives connection's default exclusive lock, usually "FOR UPDATE".
3
+ # Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
4
+ # If no record can be found for all of the listed ids, then RecordNotFound will be raised. If the primary key
5
+ # is an integer, find by id coerces its arguments using +to_i+.
49
6
  #
50
- # ==== Examples
51
- #
52
- # # find by id
53
7
  # Person.find(1) # returns the object for ID = 1
8
+ # Person.find("1") # returns the object for ID = 1
54
9
  # Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
55
10
  # Person.find([7, 17]) # returns an array for objects with IDs in (7, 17)
56
11
  # Person.find([1]) # returns an array for the object with ID = 1
57
12
  # Person.where("administrator = 1").order("created_on DESC").find(1)
58
13
  #
59
14
  # Note that returned records may not be in the same order as the ids you
60
- # provide since database rows are unordered. Give an explicit <tt>:order</tt>
15
+ # provide since database rows are unordered. Give an explicit <tt>order</tt>
61
16
  # to ensure the results are sorted.
62
17
  #
63
- # ==== Examples
64
- #
65
- # # find first
66
- # Person.first # returns the first object fetched by SELECT * FROM people
67
- # Person.where(["user_name = ?", user_name]).first
68
- # Person.where(["user_name = :u", { :u => user_name }]).first
69
- # Person.order("created_on DESC").offset(5).first
70
- #
71
- # # find last
72
- # Person.last # returns the last object fetched by SELECT * FROM people
73
- # Person.where(["user_name = ?", user_name]).last
74
- # Person.order("created_on DESC").offset(5).last
75
- #
76
- # # find all
77
- # Person.all # returns an array of objects for all the rows fetched by SELECT * FROM people
78
- # Person.where(["category IN (?)", categories]).limit(50).all
79
- # Person.where({ :friends => ["Bob", "Steve", "Fred"] }).all
80
- # Person.offset(10).limit(10).all
81
- # Person.includes([:account, :friends]).all
82
- # Person.group("category").all
18
+ # ==== Find with lock
83
19
  #
84
20
  # Example for find with a lock: Imagine two concurrent transactions:
85
21
  # each will read <tt>person.visits == 2</tt>, add 1 to it, and save, resulting
@@ -93,30 +29,62 @@ module ActiveRecord
93
29
  # person.save!
94
30
  # end
95
31
  def find(*args)
96
- return to_a.find { |*block_args| yield(*block_args) } if block_given?
97
-
98
- options = args.extract_options!
99
-
100
- if options.present?
101
- apply_finder_options(options).find(*args)
32
+ if block_given?
33
+ to_a.find { |*block_args| yield(*block_args) }
102
34
  else
103
- case args.first
104
- when :first, :last, :all
105
- send(args.first)
106
- else
107
- find_with_ids(*args)
108
- end
35
+ find_with_ids(*args)
109
36
  end
110
37
  end
111
38
 
112
- # A convenience wrapper for <tt>find(:first, *args)</tt>. You can pass in all the
113
- # same arguments to this method as you can to <tt>find(:first)</tt>.
114
- def first(*args)
115
- if args.any?
116
- if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash))
117
- limit(*args).to_a
39
+ # Finds the first record matching the specified conditions. There
40
+ # is no implied ording so if order matters, you should specify it
41
+ # yourself.
42
+ #
43
+ # If no record is found, returns <tt>nil</tt>.
44
+ #
45
+ # Post.find_by name: 'Spartacus', rating: 4
46
+ # Post.find_by "published_at < ?", 2.weeks.ago
47
+ def find_by(*args)
48
+ where(*args).take
49
+ end
50
+
51
+ # Like <tt>find_by</tt>, except that if no record is found, raises
52
+ # an <tt>ActiveRecord::RecordNotFound</tt> error.
53
+ def find_by!(*args)
54
+ where(*args).take!
55
+ end
56
+
57
+ # Gives a record (or N records if a parameter is supplied) without any implied
58
+ # order. The order will depend on the database implementation.
59
+ # If an order is supplied it will be respected.
60
+ #
61
+ # Person.take # returns an object fetched by SELECT * FROM people LIMIT 1
62
+ # Person.take(5) # returns 5 objects fetched by SELECT * FROM people LIMIT 5
63
+ # Person.where(["name LIKE '%?'", name]).take
64
+ def take(limit = nil)
65
+ limit ? limit(limit).to_a : find_take
66
+ end
67
+
68
+ # Same as +take+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
69
+ # is found. Note that <tt>take!</tt> accepts no arguments.
70
+ def take!
71
+ take or raise RecordNotFound
72
+ end
73
+
74
+ # Find the first record (or first N records if a parameter is supplied).
75
+ # If no order is defined it will order by primary key.
76
+ #
77
+ # Person.first # returns the first object fetched by SELECT * FROM people
78
+ # Person.where(["user_name = ?", user_name]).first
79
+ # Person.where(["user_name = :u", { u: user_name }]).first
80
+ # Person.order("created_on DESC").offset(5).first
81
+ # Person.first(3) # returns the first three objects fetched by SELECT * FROM people LIMIT 3
82
+ def first(limit = nil)
83
+ if limit
84
+ if order_values.empty? && primary_key
85
+ order(arel_table[primary_key].asc).limit(limit).to_a
118
86
  else
119
- apply_finder_options(args.first).first
87
+ limit(limit).to_a
120
88
  end
121
89
  else
122
90
  find_first
@@ -129,18 +97,27 @@ module ActiveRecord
129
97
  first or raise RecordNotFound
130
98
  end
131
99
 
132
- # A convenience wrapper for <tt>find(:last, *args)</tt>. You can pass in all the
133
- # same arguments to this method as you can to <tt>find(:last)</tt>.
134
- def last(*args)
135
- if args.any?
136
- if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash))
137
- if order_values.empty? && primary_key
138
- order("#{quoted_table_name}.#{quoted_primary_key} DESC").limit(*args).reverse
139
- else
140
- to_a.last(*args)
141
- end
100
+ # Find the last record (or last N records if a parameter is supplied).
101
+ # If no order is defined it will order by primary key.
102
+ #
103
+ # Person.last # returns the last object fetched by SELECT * FROM people
104
+ # Person.where(["user_name = ?", user_name]).last
105
+ # Person.order("created_on DESC").offset(5).last
106
+ # Person.last(3) # returns the last three objects fetched by SELECT * FROM people.
107
+ #
108
+ # Take note that in that last case, the results are sorted in ascending order:
109
+ #
110
+ # [#<Person id:2>, #<Person id:3>, #<Person id:4>]
111
+ #
112
+ # and not:
113
+ #
114
+ # [#<Person id:4>, #<Person id:3>, #<Person id:2>]
115
+ def last(limit = nil)
116
+ if limit
117
+ if order_values.empty? && primary_key
118
+ order(arel_table[primary_key].desc).limit(limit).reverse
142
119
  else
143
- apply_finder_options(args.first).last
120
+ to_a.last(limit)
144
121
  end
145
122
  else
146
123
  find_last
@@ -153,14 +130,8 @@ module ActiveRecord
153
130
  last or raise RecordNotFound
154
131
  end
155
132
 
156
- # A convenience wrapper for <tt>find(:all, *args)</tt>. You can pass in all the
157
- # same arguments to this method as you can to <tt>find(:all)</tt>.
158
- def all(*args)
159
- args.any? ? apply_finder_options(args.first).to_a : to_a
160
- end
161
-
162
- # Returns true if a record exists in the table that matches the +id+ or
163
- # conditions given, or false otherwise. The argument can take five forms:
133
+ # Returns +true+ if a record exists in the table that matches the +id+ or
134
+ # conditions given, or +false+ otherwise. The argument can take six forms:
164
135
  #
165
136
  # * Integer - Finds the record with this primary key.
166
137
  # * String - Finds the record with a primary key corresponding to this
@@ -168,8 +139,9 @@ module ActiveRecord
168
139
  # * Array - Finds the record that matches these +find+-style conditions
169
140
  # (such as <tt>['color = ?', 'red']</tt>).
170
141
  # * Hash - Finds the record that matches these +find+-style conditions
171
- # (such as <tt>{:color => 'red'}</tt>).
172
- # * No args - Returns false if the table is empty, true otherwise.
142
+ # (such as <tt>{color: 'red'}</tt>).
143
+ # * +false+ - Returns always +false+.
144
+ # * No args - Returns +false+ if the table is empty, +true+ otherwise.
173
145
  #
174
146
  # For more information about specifying conditions as a Hash or Array,
175
147
  # see the Conditions section in the introduction to ActiveRecord::Base.
@@ -178,28 +150,28 @@ module ActiveRecord
178
150
  # 'Jamie'</tt>), since it would be sanitized and then queried against
179
151
  # the primary key column, like <tt>id = 'name = \'Jamie\''</tt>.
180
152
  #
181
- # ==== Examples
182
153
  # Person.exists?(5)
183
154
  # Person.exists?('5')
184
- # Person.exists?(:name => "David")
185
155
  # Person.exists?(['name LIKE ?', "%#{query}%"])
156
+ # Person.exists?(name: 'David')
157
+ # Person.exists?(false)
186
158
  # Person.exists?
187
- def exists?(id = false)
188
- id = id.id if ActiveRecord::Base === id
189
- return false if id.nil?
159
+ def exists?(conditions = :none)
160
+ conditions = conditions.id if Base === conditions
161
+ return false if !conditions
190
162
 
191
163
  join_dependency = construct_join_dependency_for_association_find
192
164
  relation = construct_relation_for_association_find(join_dependency)
193
165
  relation = relation.except(:select, :order).select("1 AS one").limit(1)
194
166
 
195
- case id
167
+ case conditions
196
168
  when Array, Hash
197
- relation = relation.where(id)
169
+ relation = relation.where(conditions)
198
170
  else
199
- relation = relation.where(table[primary_key].eq(id)) if id
171
+ relation = relation.where(table[primary_key].eq(conditions)) if conditions != :none
200
172
  end
201
173
 
202
- connection.select_value(relation, "#{name} Exists") ? true : false
174
+ connection.select_value(relation, "#{name} Exists", relation.bind_values)
203
175
  rescue ThrowResult
204
176
  false
205
177
  end
@@ -216,12 +188,12 @@ module ActiveRecord
216
188
  end
217
189
 
218
190
  def construct_join_dependency_for_association_find
219
- including = (@eager_load_values + @includes_values).uniq
191
+ including = (eager_load_values + includes_values).uniq
220
192
  ActiveRecord::Associations::JoinDependency.new(@klass, including, [])
221
193
  end
222
194
 
223
195
  def construct_relation_for_association_calculations
224
- including = (@eager_load_values + @includes_values).uniq
196
+ including = (eager_load_values + includes_values).uniq
225
197
  join_dependency = ActiveRecord::Associations::JoinDependency.new(@klass, including, arel.froms.first)
226
198
  relation = except(:includes, :eager_load, :preload)
227
199
  apply_join_dependency(relation, join_dependency)
@@ -251,10 +223,9 @@ module ActiveRecord
251
223
 
252
224
  def construct_limited_ids_condition(relation)
253
225
  orders = relation.order_values.map { |val| val.presence }.compact
254
- values = @klass.connection.distinct("#{@klass.connection.quote_table_name table_name}.#{primary_key}", orders)
226
+ values = @klass.connection.distinct("#{quoted_table_name}.#{primary_key}", orders)
255
227
 
256
228
  relation = relation.dup.select(values)
257
- relation.uniq_value = nil
258
229
 
259
230
  id_rows = @klass.connection.select_all(relation.arel, 'SQL', relation.bind_values)
260
231
  ids_array = id_rows.map {|row| row[primary_key]}
@@ -262,47 +233,7 @@ module ActiveRecord
262
233
  ids_array.empty? ? raise(ThrowResult) : table[primary_key].in(ids_array)
263
234
  end
264
235
 
265
- def find_by_attributes(match, attributes, *args)
266
- conditions = Hash[attributes.map {|a| [a, args[attributes.index(a)]]}]
267
- result = where(conditions).send(match.finder)
268
-
269
- if match.bang? && result.nil?
270
- raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}"
271
- else
272
- yield(result) if block_given?
273
- result
274
- end
275
- end
276
-
277
- def find_or_instantiator_by_attributes(match, attributes, *args)
278
- options = args.size > 1 && args.last(2).all?{ |a| a.is_a?(Hash) } ? args.extract_options! : {}
279
- protected_attributes_for_create, unprotected_attributes_for_create = {}, {}
280
- args.each_with_index do |arg, i|
281
- if arg.is_a?(Hash)
282
- protected_attributes_for_create = args[i].with_indifferent_access
283
- else
284
- unprotected_attributes_for_create[attributes[i]] = args[i]
285
- end
286
- end
287
-
288
- conditions = (protected_attributes_for_create.merge(unprotected_attributes_for_create)).slice(*attributes).symbolize_keys
289
-
290
- record = where(conditions).first
291
-
292
- unless record
293
- record = @klass.new(protected_attributes_for_create, options) do |r|
294
- r.assign_attributes(unprotected_attributes_for_create, :without_protection => true)
295
- end
296
- yield(record) if block_given?
297
- record.send(match.save_method) if match.save_record?
298
- end
299
-
300
- record
301
- end
302
-
303
236
  def find_with_ids(*ids)
304
- return to_a.find { |*block_args| yield(*block_args) } if block_given?
305
-
306
237
  expects_array = ids.first.kind_of?(Array)
307
238
  return ids.first if expects_array && ids.first.empty?
308
239
 
@@ -322,21 +253,11 @@ module ActiveRecord
322
253
  def find_one(id)
323
254
  id = id.id if ActiveRecord::Base === id
324
255
 
325
- if IdentityMap.enabled? && where_values.blank? &&
326
- limit_value.blank? && order_values.blank? &&
327
- includes_values.blank? && preload_values.blank? &&
328
- readonly_value.nil? && joins_values.blank? &&
329
- !@klass.locking_enabled? &&
330
- record = IdentityMap.get(@klass, id)
331
- return record
332
- end
333
-
334
256
  column = columns_hash[primary_key]
335
-
336
- substitute = connection.substitute_at(column, @bind_values.length)
257
+ substitute = connection.substitute_at(column, bind_values.length)
337
258
  relation = where(table[primary_key].eq(substitute))
338
- relation.bind_values = [[column, id]]
339
- record = relation.first
259
+ relation.bind_values += [[column, id]]
260
+ record = relation.take
340
261
 
341
262
  unless record
342
263
  conditions = arel.where_sql
@@ -348,18 +269,18 @@ module ActiveRecord
348
269
  end
349
270
 
350
271
  def find_some(ids)
351
- result = where(table[primary_key].in(ids)).all
272
+ result = where(table[primary_key].in(ids)).to_a
352
273
 
353
274
  expected_size =
354
- if @limit_value && ids.size > @limit_value
355
- @limit_value
275
+ if limit_value && ids.size > limit_value
276
+ limit_value
356
277
  else
357
278
  ids.size
358
279
  end
359
280
 
360
281
  # 11 ids with limit 3, offset 9 should give 2 results.
361
- if @offset_value && (ids.size - @offset_value < expected_size)
362
- expected_size = ids.size - @offset_value
282
+ if offset_value && (ids.size - offset_value < expected_size)
283
+ expected_size = ids.size - offset_value
363
284
  end
364
285
 
365
286
  if result.size == expected_size
@@ -374,11 +295,24 @@ module ActiveRecord
374
295
  end
375
296
  end
376
297
 
298
+ def find_take
299
+ if loaded?
300
+ @records.first
301
+ else
302
+ @take ||= limit(1).to_a.first
303
+ end
304
+ end
305
+
377
306
  def find_first
378
307
  if loaded?
379
308
  @records.first
380
309
  else
381
- @first ||= limit(1).to_a[0]
310
+ @first ||=
311
+ if with_default_scope.order_values.empty? && primary_key
312
+ order(arel_table[primary_key].asc).limit(1).to_a.first
313
+ else
314
+ limit(1).to_a.first
315
+ end
382
316
  end
383
317
  end
384
318
 
@@ -390,7 +324,7 @@ module ActiveRecord
390
324
  if offset_value || limit_value
391
325
  to_a.last
392
326
  else
393
- reverse_order.limit(1).to_a[0]
327
+ reverse_order.limit(1).to_a.first
394
328
  end
395
329
  end
396
330
  end