diff options
Diffstat (limited to 'activerecord')
32 files changed, 464 insertions, 2076 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 7efd75a239..58f680a03f 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,1858 +1,42 @@ -* Default scopes are no longer overriden by chained conditions. +* `before_add` callbacks are fired before the record is saved on + `has_and_belongs_to_many` assocations *and* on `has_many :through` + associations. Before this change, `before_add` callbacks would be fired + before the record was saved on `has_and_belongs_to_many` associations, but + *not* on `has_many :through` associations. - Before this change when you defined a `default_scope` in a model - it was overriden by chained conditions in the same field. Now it - is merged like any other scope. + Fixes #14144 - Before: +* Fixed STI classes not defining an attribute method if there is a + conflicting private method defined on its ancestors. - class User < ActiveRecord::Base - default_scope { where state: 'pending' } - scope :active, -> { where state: 'active' } - scope :inactive, -> { where state: 'inactive' } - end - - User.all - # SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' - - User.active - # SELECT "users".* FROM "users" WHERE "users"."state" = 'active' - - User.where(state: 'inactive') - # SELECT "users".* FROM "users" WHERE "users"."state" = 'inactive' - - After: - - class User < ActiveRecord::Base - default_scope { where state: 'pending' } - scope :active, -> { where state: 'active' } - scope :inactive, -> { where state: 'inactive' } - end - - User.all - # SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' - - User.active - # SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' AND "users"."state" = 'active' - - User.where(state: 'inactive') - # SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' AND "users"."state" = 'inactive' - - To get the previous behavior it is needed to explicitly remove the - `default_scope` condition using `unscoped`, `unscope`, `rewhere` or - `except`. - - Example: - - class User < ActiveRecord::Base - default_scope { where state: 'pending' } - scope :active, -> { unscope(where: :state).where(state: 'active') } - scope :inactive, -> { rewhere state: 'inactive' } - end - - User.all - # SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' - - User.active - # SELECT "users".* FROM "users" WHERE "users"."state" = 'active' - - User.inactive - # SELECT "users".* FROM "users" WHERE "users"."state" = 'inactive' - -* Perform necessary deeper encoding when hstore is inside an array. - - Fixes #11135. - - *Josh Goodall*, *Genadi Samokovarov* - -* Properly detect if a connection is still active before using it - in multi-threaded environments. - - Fixes #12867. - - *Kevin Casey*, *Matthew Draper*, *William (B.J.) Snow Orvis* - -* When inverting add_index use the index name if present instead of - the columns. - - If there are two indices with matching columns and one of them is - explicitly named then reverting the migration adding the named one - would instead drop the unnamed one. - - The inversion of add_index will now drop the index by its name if - it is present. - - *Hubert Dąbrowski* - -* Add flag to disable schema dump after migration. - - Add a config parameter on Active Record named `dump_schema_after_migration` - which is true by default. Now schema dump does not happen at the - end of migration rake task if `dump_schema_after_migration` is false. - - *Emil Soman* - -* `find_in_batches`, `find_each`, `Result#each` and `Enumerable#index_by` now - return an `Enumerator` that can calculate its size. - - See also #13938. - - *Marc-André Lafortune* - -* Make sure transaction state gets reset after a commit operation on the record. - - If a new transaction was open inside a callback, the record was loosing track - of the transaction level state, and it was leaking that state. - - Fixes #12566. - - *arthurnn* - -* Pass `has_and_belongs_to_many` `:autosave` option to - the underlying `has_many :through` association. - - Fixes #13923. - - *Yves Senn* - -* PostgreSQL implementation of `SchemaStatements#index_name_exists?`. - - The database agnostic implementation does not detect with indexes that are - not supported by the ActiveRecord schema dumper. For example, expressions - indexes would not be detected. - - Fixes #11018. - - *Jonathan Baudanza* - -* Parsing PostgreSQL arrays with empty strings now works correctly. - - Previously, if you tried to parse `{"1","","2","","3"}` the result - would be `["1","2","3"]`, removing the empty strings from the array, - which would be incorrect. Now it will correctly produce `["1","","2","","3"]` - as the result of parsing the above PostgreSQL array. - - Fixes #13907. - - *Maurício Linhares* - -* Associations now raise `ArgumentError` on name conflicts. - - Dangerous association names conflicts include instance or class methods already - defined by `ActiveRecord::Base`. - - Example: - - class Car < ActiveRecord::Base - has_many :errors - end - # Will raise ArgumentError. - - Fixes #13217. - - *Lauro Caetano* - -* Fix regressions on `select_*` methods. - When `select_*` methods receive a `Relation` object, they should be able to - get the arel/binds from it. - Also fix regressions on `select_rows` that was ignoring the binds. - - Fixes #7538, #12017, #13731, #12056. - - *arthurnn* - -* Active Record objects can now be correctly dumped, loaded and dumped again - without issues. - - Previously, if you did `YAML.dump`, `YAML.load` and then `YAML.dump` again - in an Active Record model that used serialization it would fail at the last - dump due to the fields not being correctly serialized before being dumped - to YAML. Now it is possible to dump and load the same object as many times - as needed without any issues. - - Fixes #13861. - - *Maurício Linhares* - -* `find_in_batches` now returns an `Enumerator` when called without a block, so that it - can be chained with other `Enumerable` methods. - - *Marc-André Lafortune* - -* `enum` now raises on "dangerous" name conflicts. - - Dangerous name conflicts includes instance or class method conflicts - with methods defined within `ActiveRecord::Base` but not its ancestors, - as well as conflicts with methods generated by other enums on the same - class. - - Fixes #13389. - - *Godfrey Chan* - -* `scope` now raises on "dangerous" name conflicts. - - Similar to dangerous attribute methods, a scope name conflict is - dangerous if it conflicts with an existing class method defined within - `ActiveRecord::Base` but not its ancestors. - - See also #13389. - - *Godfrey Chan*, *Philippe Creux* - -* Correctly send an user provided statement to a `lock!()` call. - - person.lock! 'FOR SHARE NOWAIT' - # Before: SELECT * ... LIMIT 1 FOR UPDATE - # After: SELECT * ... LIMIT 1 FOR SHARE NOWAIT - - Fixes #13788. - - *Maurício Linhares* - -* Handle aliased attributes `select()`, `order()` and `reorder()`. - - *Tsutomu Kuroda* - -* Reset the collection association when calling `reset` on it. - - Before: - - post.comments.loaded? # => true - post.comments.reset - post.comments.loaded? # => true - - After: - - post.comments.loaded? # => true - post.comments.reset - post.comments.loaded? # => false - - Fixes #13777. - - *Kelsey Schlarman* - -* Make enum fields work as expected with the `ActiveModel::Dirty` API. - - Before this change, using the dirty API would have surprising results: - - conversation = Conversation.new - conversation.status = :active - conversation.status = :archived - conversation.status_was # => 0 - - After this change, the same code would result in: - - conversation = Conversation.new - conversation.status = :active - conversation.status = :archived - conversation.status_was # => "active" - - *Rafael Mendonça França* - -* `has_one` and `belongs_to` accessors don't add ORDER BY to the queries - anymore. - - Since Rails 4.0, we add an ORDER BY in the `first` method to ensure - consistent results among different database engines. But for singular - associations this behavior is not needed since we will have one record to - return. As this ORDER BY option can lead some performance issues we are - removing it for singular associations accessors. - - Fixes #12623. - - *Rafael Mendonça França* - -* Prepend table name for column names passed to `Relation#select`. - - Example: - - Post.select(:id) - # Before: => SELECT id FROM "posts" - # After: => SELECT "posts"."id" FROM "posts" - - *Yves Senn* - -* Fail early with "Primary key not included in the custom select clause" - in `find_in_batches`. - - Before this patch, the exception was raised after the first batch was - yielded to the block. This means that you only get it, when you hit the - `batch_size` treshold. This could shadow the issue in development. - - *Alexander Balashov* - -* Ensure `second` through `fifth` methods act like the `first` finder. - - The famous ordinal Array instance methods defined in ActiveSupport - (`first`, `second`, `third`, `fourth`, and `fifth`) are now available as - full-fledged finders in ActiveRecord. The biggest benefit of this is ordering - of the records returned now defaults to the table's primary key in ascending order. - - Fixes #13743. - - Example: - - User.all.second - - # Before - # => 'SELECT "users".* FROM "users"' - - # After - # => SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1 OFFSET 1' - - User.offset(3).second - - # Before - # => 'SELECT "users".* FROM "users" LIMIT -1 OFFSET 3' # sqlite3 gem - # => 'SELECT "users".* FROM "users" OFFSET 3' # pg gem - # => 'SELECT `users`.* FROM `users` LIMIT 18446744073709551615 OFFSET 3' # mysql2 gem - - # After - # => SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1 OFFSET 4' - - *Jason Meller* - -* ActiveRecord states are now correctly restored after a rollback for - models that did not define any transactional callbacks (i.e. - `after_commit`, `after_rollback` or `after_create`). - - Fixes #13744. - - *Godfrey Chan* - -* Make `touch` fire the `after_commit` and `after_rollback` callbacks. - - *Harry Brundage* - -* Enable partial indexes for `sqlite >= 3.8.0`. - - See http://www.sqlite.org/partialindex.html - - *Cody Cutrer* - -* Don't try to get the subclass if the inheritance column doesn't exist - - The `subclass_from_attrs` method is called even if the column specified by - the `inheritance_column` setting doesn't exist. This prevents setting associations - via the attributes hash if the association name clashes with the value of the setting, - typically `:type`. This worked previously in Rails 3.2. - - *Ujjwal Thaakar* - -* Enum mappings are now exposed via class methods instead of constants. - - Example: - - class Conversation < ActiveRecord::Base - enum status: [ :active, :archived ] - end - - Before: - - Conversation::STATUS # => { "active" => 0, "archived" => 1 } - - After: - - Conversation.statuses # => { "active" => 0, "archived" => 1 } - - *Godfrey Chan* - -* Set `NameError#name` when STI-class-lookup fails. - - *Chulki Lee* - -* Fix bug in `becomes!` when changing from the base model to a STI sub-class. - - Fixes #13272. - - *the-web-dev*, *Yves Senn* - -* Currently Active Record can be configured via the environment variable - `DATABASE_URL` or by manually injecting a hash of values which is what Rails does, - reading in `database.yml` and setting Active Record appropriately. Active Record - expects to be able to use `DATABASE_URL` without the use of Rails, and we cannot - rip out this functionality without deprecating. This presents a problem though - when both config is set, and a `DATABASE_URL` is present. Currently the - `DATABASE_URL` should "win" and none of the values in `database.yml` are - used. This is somewhat unexpected, if one were to set values such as - `pool` in the `production:` group of `database.yml` they are ignored. - - There are many ways that Active Record initiates a connection today: - - - Stand Alone (without rails) - - `rake db:<tasks>` - - `ActiveRecord.establish_connection` - - - With Rails - - `rake db:<tasks>` - - `rails <server> | <console>` - - `rails dbconsole` - - Now all of these behave exactly the same way. The best way to do - this is to put all of this logic in one place so it is guaranteed to be used. - - Here is the matrix of how this behavior works: - - ``` - No database.yml - No DATABASE_URL - => Error - ``` - - ``` - database.yml present - No DATABASE_URL - => Use database.yml configuration - ``` - - ``` - No database.yml - DATABASE_URL present - => use DATABASE_URL configuration - ``` - - ``` - database.yml present - DATABASE_URL present - => Merged into `url` sub key. If both specify `url` sub key, the `database.yml` `url` - sub key "wins". If other paramaters `adapter` or `database` are specified in YAML, - they are discarded as the `url` sub key "wins". - ``` - - Current implementation uses `ActiveRecord::Base.configurations` to resolve and merge - all connection information before returning. This is achieved through a utility - class: `ActiveRecord::ConnectionHandling::MergeAndResolveDefaultUrlConfig`. - - To understand the exact behavior of this class, it is best to review the - behavior in `activerecord/test/cases/connection_adapters/connection_handler_test.rb`. - - *Richard Schneeman* - -* Make `change_column_null` revertable. Fixes #13576. - - *Yves Senn*, *Nishant Modak*, *Prathamesh Sonpatki* - -* Don't create/drop the test database if RAILS_ENV is specified explicitly. - - Previously, when the environment was development, we would always - create or drop both the test and development databases. - - Now, if RAILS_ENV is explicitly defined as development, we don't create - the test database. - - *Damien Mathieu* - -* Initialize version on Migration objects so that it can be used in a migration, - and it will be included in the announce message. - - *Dylan Thacker-Smith* - -* `change_table` now uses the current adapter's `update_table_definition` - method to retrieve a specific table definition. - This ensures that `change_table` and `create_table` will use - similar objects. - - Fixes #13577, #13503. - - *Nishant Modak*, *Prathamesh Sonpatki*, *Rafael Mendonça França* - -* Fixed ActiveRecord::Store nil conversion TypeError when using YAML coder. - In case the YAML passed as paramter is nil, uses an empty string. - - Fixes #13570. - - *Thales Oliveira* - -* Deprecate unused `ActiveRecord::Base.symbolized_base_class` - and `ActiveRecord::Base.symbolized_sti_name` without replacement. - - *Yves Senn* - -* Since the `test_help.rb` file in Railties now automatically maintains - your test schema, the `rake db:test:*` tasks are deprecated. This - doesn't stop you manually running other tasks on your test database - if needed: - - rake db:schema:load RAILS_ENV=test - - *Jon Leighton* - -* Fix presence validator for association when the associated record responds to `to_a`. - - *gmarik* - -* Fixed regression on preload/includes with multiple arguments failing in certain conditions, - raising a NoMethodError internally by calling `reflect_on_association` for `NilClass:Class`. - - Fixes #13437. - - *Vipul A M*, *khustochka* - -* Add the ability to nullify the `enum` column. - - Example: - - class Conversation < ActiveRecord::Base - enum gender: [:female, :male] - end - - Conversation::GENDER # => { female: 0, male: 1 } - - # conversation.update! gender: 0 - conversation.female! - conversation.female? # => true - conversation.gender # => "female" - - # conversation.update! gender: nil - conversation.gender = nil - conversation.gender.nil? # => true - conversation.gender # => nil - - *Amr Tamimi* - -* Connection specification now accepts a "url" key. The value of this - key is expected to contain a database URL. The database URL will be - expanded into a hash and merged. - - *Richard Schneeman* - -* An `ArgumentError` is now raised on a call to `Relation#where.not(nil)`. - - Example: - - User.where.not(nil) - - # Before - # => 'SELECT `users`.* FROM `users` WHERE (NOT (NULL))' - - # After - # => ArgumentError, 'Invalid argument for .where.not(), got nil.' - - *Kuldeep Aggarwal* - -* Deprecated use of string argument as a configuration lookup in - `ActiveRecord::Base.establish_connection`. Instead, a symbol must be given. - - *José Valim* - -* Fixed `update_column`, `update_columns`, and `update_all` to correctly serialize - values for `array`, `hstore` and `json` column types in PostgreSQL. - - Fixes #12261. - - *Tadas Tamosauskas*, *Carlos Antonio da Silva* - -* Do not consider PostgreSQL array columns as number or text columns. - - The code uses these checks in several places to know what to do with a - particular column, for instance AR attribute query methods has a branch - like this: - - if column.number? - !value.zero? - end - - This should never be true for array columns, since it would be the same - as running [].zero?, which results in a NoMethodError exception. - - Fixing this by ensuring that array columns in PostgreSQL never return - true for number?/text? checks. - - *Carlos Antonio da Silva* - -* When connecting to a non-existant database, the error: - `ActiveRecord::NoDatabaseError` will now be raised. When being used with Rails - the error message will include information on how to create a database: - `rake db:create`. Supported adapters: postgresql, mysql, mysql2, sqlite3 - - *Richard Schneeman* - -* Do not raise `'cannot touch on a new record object'` exception on destroying - already destroyed `belongs_to` association with `touch: true` option. - - Fixes #13445. - - Example: - - # Given Comment has belongs_to :post, touch: true - comment.post.destroy - comment.destroy # no longer raises an error - - *Paul Nikitochkin* - -* Fix a bug when assigning an array containing string numbers to a - PostgreSQL integer array column. - - Fixes #13444. - - Example: - - # Given Book#ratings is of type :integer, array: true - Book.new(ratings: [1, 2]) # worked before - Book.new(ratings: ['1', '2']) # now works as well - - *Damien Mathieu* - -* Fix `PostgreSQL` insert to properly extract table name from multiline string SQL. - - Previously, executing an insert SQL in `PostgreSQL` with a command like this: - - insert into articles( - number) - values( - 5152 - ) - - would not work because the adapter was unable to extract the correct `articles` - table name. - - *Kuldeep Aggarwal* - -* Correctly escape PostgreSQL arrays. - - Fixes: CVE-2014-0080 - -* `Relation` no longer has mutator methods like `#map!` and `#delete_if`. Convert - to an `Array` by calling `#to_a` before using these methods. - - It intends to prevent odd bugs and confusion in code that call mutator - methods directly on the `Relation`. - - Example: - - # Instead of this - Author.where(name: 'Hank Moody').compact! - - # Now you have to do this - authors = Author.where(name: 'Hank Moody').to_a - authors.compact! - - *Lauro Caetano* - -* Better support for `where()` conditions that use a `belongs_to` - association name. - - Using the name of an association in `where` previously worked only - if the value was a single `ActiveRecord::Base` object. e.g. - - Post.where(author: Author.first) - - Any other values, including `nil`, would cause invalid SQL to be - generated. This change supports arguments in the `where` query - conditions where the key is a `belongs_to` association name and the - value is `nil`, an `Array` of `ActiveRecord::Base` objects, or an - `ActiveRecord::Relation` object. - - class Post < ActiveRecord::Base - belongs_to :author - end - - `nil` value finds records where the association is not set: - - Post.where(author: nil) - # SELECT "posts".* FROM "posts" WHERE "posts"."author_id" IS NULL - - `Array` values find records where the association foreign key - matches the ids of the passed ActiveRecord models, resulting - in the same query as `Post.where(author_id: [1,2])`: - - authors_array = [Author.find(1), Author.find(2)] - Post.where(author: authors_array) - # SELECT "posts".* FROM "posts" WHERE "posts"."author_id" IN (1, 2) - - `ActiveRecord::Relation` values find records using the same - query as `Post.where(author_id: Author.where(last_name: "Emde"))` - - Post.where(author: Author.where(last_name: "Emde")) - # SELECT "posts".* FROM "posts" - # WHERE "posts"."author_id" IN ( - # SELECT "authors"."id" FROM "authors" - # WHERE "authors"."last_name" = 'Emde') - - Polymorphic `belongs_to` associations will continue to be handled - appropriately, with the polymorphic `association_type` field added - to the query to match the base class of the value. This feature - previously only worked when the value was a single `ActveRecord::Base`. - - class Post < ActiveRecord::Base - belongs_to :author, polymorphic: true - end - - Post.where(author: Author.where(last_name: "Emde")) - # Generates a query similar to: - Post.where(author_id: Author.where(last_name: "Emde"), author_type: "Author") - - *Martin Emde* - -* Respect temporary option when dropping tables with MySQL. - - Normal DROP TABLE also works, but commits the transaction. - - drop_table :temporary_table, temporary: true - - *Cody Cutrer* - -* Add option to create tables from a query. - - create_table(:long_query, temporary: true, - as: "SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id") - - Generates: - - CREATE TEMPORARY TABLE long_query AS - SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id - - *Cody Cutrer* - -* `db:test:clone` and `db:test:prepare` must load Rails environment. - - `db:test:clone` and `db:test:prepare` use `ActiveRecord::Base`. configurations, - so we need to load the Rails environment, otherwise the config wont be in place. - - *arthurnn* - -* Use the right column to type cast grouped calculations with custom expressions. - - Fixes #13230. - - Example: - - # Before - Account.group(:firm_name).sum('0.01 * credit_limit') - # => { '37signals' => '0.5' } - - # After - Account.group(:firm_name).sum('0.01 * credit_limit') - # => { '37signals' => 0.5 } - - *Paul Nikitochkin* - -* Polymorphic `belongs_to` associations with the `touch: true` option set update the timestamps of - the old and new owner correctly when moved between owners of different types. - - Example: - - class Rating < ActiveRecord::Base - belongs_to :rateable, polymorphic: true, touch: true - end - - rating = Rating.create rateable: Song.find(1) - rating.update_attributes rateable: Book.find(2) # => timestamps of Song(1) and Book(2) are updated - - *Severin Schoepke* - -* Improve formatting of migration exception messages: make them easier to read - with line breaks before/after, and improve the error for pending migrations. - - *John Bachir* - -* Fix `last` with `offset` to return the proper record instead of always the last one. - - Example: - - Model.offset(4).last - # => returns the 4th record from the end. - - Fixes #7441. - - *kostya*, *Lauro Caetano* - -* `type_to_sql` returns a `String` for unmapped columns. This fixes an error - when using unmapped PostgreSQL array types. - - Example: - - change_colum :table, :column, :bigint, array: true - - Fixes #13146. - - *Jens Fahnenbruck*, *Yves Senn* - -* Fix `QueryCache` to work with nested blocks, so that it will only clear the existing cache - after leaving the outer block instead of clearing it right after the inner block is finished. - - *Vipul A M* - -* The ERB in fixture files is no longer evaluated in the context of the main - object. Helper methods used by multiple fixtures should be defined on the - class object returned by `ActiveRecord::FixtureSet.context_class`. - - *Victor Costan* - -* Previously, the `has_one` macro incorrectly accepted the `counter_cache` - option, but never actually supported it. Now it will raise an `ArgumentError` - when using `has_one` with `counter_cache`. + Fixes #11569. *Godfrey Chan* -* Implement `rename_index` natively for MySQL >= 5.7. - - *Cody Cutrer* - -* Fix bug when validating the uniqueness of an aliased attribute. - - Fixes #12402. - - *Lauro Caetano* - -* Update counter cache on a `has_many` relationship regardless of default scope. - - Fixes #12952. - - *Uku Taht* - -* `rename_index` adds the new index before removing the old one. This allows to - rename indexes on columns with a foreign key and prevents the following error: - - Cannot drop index 'index_engines_on_car_id': needed in a foreign key constraint - - *Cody Cutrer*, *Yves Senn* - -* Raise `ActiveRecord::RecordNotDestroyed` when a replaced child - marked with `dependent: destroy` fails to be destroyed. - - Fixes #12812. - - *Brian Thomas Storti* - -* Fix validation on uniqueness of empty association. - - *Evgeny Li* - -* Make `ActiveRecord::Relation#unscope` affect relations it is merged in to. - - *Jon Leighton* - -* Use strings to represent non-string `order_values`. - - *Yves Senn* - -* Checks to see if the record contains the foreign key to set the inverse automatically. - - *Edo Balvers* - -* Added `ActiveRecord::Base.to_param` for convenient "pretty" URLs derived from a model's attribute or method. - - Example: - - class User < ActiveRecord::Base - to_param :name - end - - user = User.find_by(name: 'Fancy Pants') - user.id # => 123 - user.to_param # => "123-fancy-pants" - - *Javan Makhmali* - -* Added `ActiveRecord::Base.no_touching`, which allows ignoring touch on models. - - Example: - - Post.no_touching do - Post.first.touch - end - - *Sam Stephenson*, *Damien Mathieu* - -* Prevent the counter cache from being decremented twice when destroying - a record on a `has_many :through` association. - - Fixes #11079. - - *Dmitry Dedov* - -* Unify boolean type casting for `MysqlAdapter` and `Mysql2Adapter`. - `type_cast` will return `1` for `true` and `0` for `false`. - - Fixes #11119. - - *Adam Williams*, *Yves Senn* - -* Fix bug where `has_one` association record update result in crash, when replaced with itself. - - Fixes #12834. - - *Denis Redozubov*, *Sergio Cambra* - -* Log bind variables after they are type casted. This makes it more - transparent what values are actually sent to the database. - - irb(main):002:0> Event.find("im-no-integer") - # Before: ... WHERE "events"."id" = $1 LIMIT 1 [["id", "im-no-integer"]] - # After: ... WHERE "events"."id" = $1 LIMIT 1 [["id", 0]] - - *Yves Senn* - -* Fix uninitialized constant `TransactionState` error when `Marshall.load` is used on an Active Record result. - - Fixes #12790. - - *Jason Ayre* - -* `.unscope` now removes conditions specified in `default_scope`. - - *Jon Leighton* - -* Added `ActiveRecord::QueryMethods#rewhere` which will overwrite an existing, named where condition. - - Examples: - - Post.where(trashed: true).where(trashed: false) #=> WHERE `trashed` = 1 AND `trashed` = 0 - Post.where(trashed: true).rewhere(trashed: false) #=> WHERE `trashed` = 0 - Post.where(active: true).where(trashed: true).rewhere(trashed: false) #=> WHERE `active` = 1 AND `trashed` = 0 - - *DHH* - -* Extend `ActiveRecord::Base#cache_key` to take an optional list of timestamp attributes of which the highest will be used. - - Example: - - # last_reviewed_at will be used, if that's more recent than updated_at, or vice versa - Person.find(5).cache_key(:updated_at, :last_reviewed_at) - - *DHH* - -* Added `ActiveRecord::Base#enum` for declaring enum attributes where the values map to integers in the database, but can be queried by name. - - Example: - - class Conversation < ActiveRecord::Base - enum status: [:active, :archived] - end - - Conversation::STATUS # => { active: 0, archived: 1 } - - # conversation.update! status: 0 - conversation.active! - conversation.active? # => true - conversation.status # => "active" - - # conversation.update! status: 1 - conversation.archived! - conversation.archived? # => true - conversation.status # => "archived" - - # conversation.update! status: 1 - conversation.status = :archived - - *DHH* - -* `ActiveRecord::Base#attribute_for_inspect` now truncates long arrays (more than 10 elements). - - *Jan Bernacki* - -* Allow for the name of the `schema_migrations` table to be configured. - - *Jerad Phelps* - -* Do not add to scope includes values from through associations. - Fixed bug when providing `includes` in through association scope, and fetching targets. - - Example: - - class Vendor < ActiveRecord::Base - has_many :relationships, -> { includes(:user) } - has_many :users, through: :relationships - end - - vendor = Vendor.first - - # Before - - vendor.users.to_a # => Raises exception: not found `:user` for `User` - - # After - - vendor.users.to_a # => No exception is raised - - Fixes #12242, #9517, #10240. - - *Paul Nikitochkin* - -* Type cast json values on write, so that the value is consistent - with reading from the database. - - Example: - - x = JsonDataType.new tags: {"string" => "foo", :symbol => :bar} - - # Before: - x.tags # => {"string" => "foo", :symbol => :bar} - - # After: - x.tags # => {"string" => "foo", "symbol" => "bar"} - - *Severin Schoepke* - -* `ActiveRecord::Store` works together with PostgreSQL `hstore` columns. - - Fixes #12452. - - *Yves Senn* - -* Fix bug where `ActiveRecord::Store` used a global `Hash` to keep track of - all registered `stored_attributes`. Now every subclass of - `ActiveRecord::Base` has it's own `Hash`. - - *Yves Senn* - -* Save `has_one` association when primary key is manually set. - - Fixes #12302. - - *Lauro Caetano* - -* Allow any version of BCrypt when using `has_secure_password`. - - *Mike Perham* - -* Sub-query generated for `Relation` passed as array condition did not take in account - bind values and have invalid syntax. - - Generate sub-query with inline bind values. - - Fixes #12586. - - *Paul Nikitochkin* - -* Fix a bug where rake db:structure:load crashed when the path contained - spaces. - - *Kevin Mook* - -* `ActiveRecord::QueryMethods#unscope` unscopes negative equality - - Allows you to call `#unscope` on a relation with negative equality - operators, i.e. `Arel::Nodes::NotIn` and `Arel::Nodes::NotEqual` that have - been generated through the use of `where.not`. - - *Eric Hankins* - -* Raise an exception when model without primary key calls `.find_with_ids`. - - *Shimpei Makimoto* - -* Make `Relation#empty?` use `exists?` instead of `count`. - - *Szymon Nowak* - -* `rake db:structure:dump` no longer crashes when the port was specified as `Fixnum`. - - *Kenta Okamoto* - -* `NullRelation#pluck` takes a list of columns - - The method signature in `NullRelation` was updated to mimic that in - `Calculations`. - - *Derek Prior* - -* `scope_chain` should not be mutated for other reflections. - - Currently `scope_chain` uses same array for building different - `scope_chain` for different associations. During processing - these arrays are sometimes mutated and because of in-place - mutation the changed `scope_chain` impacts other reflections. - - Fix is to dup the value before adding to the `scope_chain`. - - Fixes #3882. - - *Neeraj Singh* - -* Prevent the inversed association from being reloaded on save. - - Fixes #9499. - - *Dmitry Polushkin* - -* Generate subquery for `Relation` if it passed as array condition for `where` - method. - - Example: - - # Before - Blog.where('id in (?)', Blog.where(id: 1)) - # => SELECT "blogs".* FROM "blogs" WHERE "blogs"."id" = 1 - # => SELECT "blogs".* FROM "blogs" WHERE (id IN (1)) - - # After - Blog.where('id in (?)', Blog.where(id: 1).select(:id)) - # => SELECT "blogs".* FROM "blogs" - # WHERE "blogs"."id" IN (SELECT "blogs"."id" FROM "blogs" WHERE "blogs"."id" = 1) - - Fixes #12415. - - *Paul Nikitochkin* - -* For missed association exception message - which is raised in `ActiveRecord::Associations::Preloader` class - added owner record class name in order to simplify to find problem code. - - *Paul Nikitochkin* - -* `has_and_belongs_to_many` is now transparently implemented in terms of - `has_many :through`. Behavior should remain the same, if not, it is a bug. - -* `create_savepoint`, `rollback_to_savepoint` and `release_savepoint` accept - a savepoint name. - - *Yves Senn* - -* Make `next_migration_number` accessible for third party generators. - - *Yves Senn* - -* Objects instantiated using a null relationship will now retain the - attributes of the where clause. - - Fixes #11676, #11675, #11376. - - *Paul Nikitochkin*, *Peter Brown*, *Nthalk* - -* Fixed `ActiveRecord::Associations::CollectionAssociation#find` - when using `has_many` association with `:inverse_of` and finding an array of one element, - it should return an array of one element too. - - *arthurnn* - -* Callbacks on has_many should access the in memory parent if a inverse_of is set. - - *arthurnn* - -* `ActiveRecord::ConnectionAdapters.string_to_time` respects - string with timezone (e.g. Wed, 04 Sep 2013 20:30:00 JST). - - Fixes #12278. - - *kennyj* - -* Calling `update_attributes` will now throw an `ArgumentError` whenever it - gets a `nil` argument. More specifically, it will throw an error if the - argument that it gets passed does not respond to to `stringify_keys`. - - Example: - - @my_comment.update_attributes(nil) # => raises ArgumentError - - *John Wang* - -* Deprecate `quoted_locking_column` method, which isn't used anywhere. - - *kennyj* - -* Migration dump UUID default functions to schema.rb. - - Fixes #10751. - - *kennyj* - -* Fixed a bug in `ActiveRecord::Associations::CollectionAssociation#find_by_scan` - when using `has_many` association with `:inverse_of` option and UUID primary key. - - Fixes #10450. - - *kennyj* - -* Fix: joins association, with defined in the scope block constraints by using several - where constraints and at least of them is not `Arel::Nodes::Equality`, - generates invalid SQL expression. - - Fixes #11963. - - *Paul Nikitochkin* - -* `CollectionAssociation#first`/`#last` (e.g. `has_many`) use a `LIMIT`ed - query to fetch results rather than loading the entire collection. - - *Lann Martin* - -* Make possible to run SQLite rake tasks without the `Rails` constant defined. - - *Damien Mathieu* - -* Allow Relation#from to accept other relations with bind values. - - *Ryan Wallace* - -* Fix inserts with prepared statements disabled. - - Fixes #12023. - - *Rafael Mendonça França* - -* Setting a has_one association on a new record no longer causes an empty - transaction. - - *Dylan Thacker-Smith* - -* Fix `AR::Relation#merge` sometimes failing to preserve `readonly(false)` flag. - - *thedarkone* - -* Re-use `order` argument pre-processing for `reorder`. - - *Paul Nikitochkin* - -* Fix PredicateBuilder so polymorphic association keys in `where` clause can - accept objects other than direct descendants of `ActiveRecord::Base` (decorated - models, for example). - - *Mikhail Dieterle* - -* PostgreSQL adapter recognizes negative money values formatted with - parentheses (eg. `($1.25) # => -1.25`)). - Fixes #11899. - - *Yves Senn* - -* Stop interpreting SQL 'string' columns as :string type because there is no - common STRING datatype in SQL. - - *Ben Woosley* - -* `ActiveRecord::FinderMethods#exists?` returns `true`/`false` in all cases. - - *Xavier Noria* - -* Assign inet/cidr attribute with `nil` value for invalid address. - - Example: - - record = User.new - record.logged_in_from_ip # is type of an inet or a cidr - - # Before: - record.logged_in_from_ip = 'bad ip address' # raise exception - - # After: - record.logged_in_from_ip = 'bad ip address' # do not raise exception - record.logged_in_from_ip # => nil - record.logged_in_from_ip_before_type_cast # => 'bad ip address' - - *Paul Nikitochkin* - -* `add_to_target` now accepts a second optional `skip_callbacks` argument - - If truthy, it will skip the :before_add and :after_add callbacks. - - *Ben Woosley* - -* Fix interactions between `:before_add` callbacks and nested attributes - assignment of `has_many` associations, when the association was not - yet loaded: - - - A `:before_add` callback was being called when a nested attributes - assignment assigned to an existing record. - - - Nested Attributes assignment did not affect the record in the - association target when a `:before_add` callback triggered the - loading of the association - - *Jörg Schray* - -* Allow enable_extension migration method to be revertible. - - *Eric Tipton* - -* Type cast hstore values on write, so that the value is consistent - with reading from the database. - - Example: - - x = Hstore.new tags: {"bool" => true, "number" => 5} - - # Before: - x.tags # => {"bool" => true, "number" => 5} - - # After: - x.tags # => {"bool" => "true", "number" => "5"} - - *Yves Senn* , *Severin Schoepke* - -* Fix multidimensional PostgreSQL arrays containing non-string items. - - *Yves Senn* - -* Fixes bug when using includes combined with select, the select statement was overwritten. - - Fixes #11773. - - *Edo Balvers* - -* Load fixtures from linked folders. - - *Kassio Borges* - -* Create a directory for sqlite3 file if not present on the system. - - *Richard Schneeman* - -* Removed redundant override of `xml` column definition for PostgreSQL, - in order to use `xml` column type instead of `text`. - - *Paul Nikitochkin*, *Michael Nikitochkin* - -* Revert `ActiveRecord::Relation#order` change that make new order - prepend the old one. - - Before: - - User.order("name asc").order("created_at desc") - # SELECT * FROM users ORDER BY created_at desc, name asc - - After: - - User.order("name asc").order("created_at desc") - # SELECT * FROM users ORDER BY name asc, created_at desc - - This also affects order defined in `default_scope` or any kind of associations. - -* Add ability to define how a class is converted to Arel predicates. - For example, adding a very vendor specific regex implementation: - - regex_handler = proc do |column, value| - Arel::Nodes::InfixOperation.new('~', column, value.source) - end - ActiveRecord::PredicateBuilder.register_handler(Regexp, regex_handler) - - *Sean Griffin & @joannecheng* - -* Don't allow `quote_value` to be called without a column. - - Some adapters require column information to do their job properly. - By enforcing the provision of the column for this internal method - we ensure that those using adapters that require column information - will always get the proper behavior. - - *Ben Woosley* - -* When using optimistic locking, `update` was not passing the column to `quote_value` - to allow the connection adapter to properly determine how to quote the value. This was - affecting certain databases that use specific column types. - - Fixes #6763. - - *Alfred Wong* - -* rescue from all exceptions in `ConnectionManagement#call` - - Fixes #11497. - - As `ActiveRecord::ConnectionAdapters::ConnectionManagement` middleware does - not rescue from Exception (but only from StandardError), the Connection - Pool quickly runs out of connections when multiple erroneous Requests come - in right after each other. - - Rescuing from all exceptions and not just StandardError, fixes this - behaviour. - - *Vipul A M* - -* `change_column` for PostgreSQL adapter respects the `:array` option. - - *Yves Senn* - -* Remove deprecation warning from `attribute_missing` for attributes that are columns. - - *Arun Agrawal* - -* Remove extra decrement of transaction deep level. - - Fixes #4566. - - *Paul Nikitochkin* - -* Reset @column_defaults when assigning `locking_column`. - We had a potential problem. For example: - - class Post < ActiveRecord::Base - self.column_defaults # if we call this unintentionally before setting locking_column ... - self.locking_column = 'my_locking_column' - end - - Post.column_defaults["my_locking_column"] - => nil # expected value is 0 ! - - *kennyj* - -* Remove extra select and update queries on save/touch/destroy ActiveRecord model - with belongs to reflection with option `touch: true`. - - Fixes #11288. - - *Paul Nikitochkin* - -* Remove deprecated nil-passing to the following `SchemaCache` methods: - `primary_keys`, `tables`, `columns` and `columns_hash`. - - *Yves Senn* - -* Remove deprecated block filter from `ActiveRecord::Migrator#migrate`. - - *Yves Senn* - -* Remove deprecated String constructor from `ActiveRecord::Migrator`. - - *Yves Senn* - -* Remove deprecated `scope` use without passing a callable object. - - *Arun Agrawal* - -* Remove deprecated `transaction_joinable=` in favor of `begin_transaction` - with `:joinable` option. - - *Arun Agrawal* - -* Remove deprecated `decrement_open_transactions`. - - *Arun Agrawal* - -* Remove deprecated `increment_open_transactions`. - - *Arun Agrawal* - -* Remove deprecated `PostgreSQLAdapter#outside_transaction?` - method. You can use `#transaction_open?` instead. - - *Yves Senn* - -* Remove deprecated `ActiveRecord::Fixtures.find_table_name` in favor of - `ActiveRecord::Fixtures.default_fixture_model_name`. - - *Vipul A M* - -* Removed deprecated `columns_for_remove` from `SchemaStatements`. - - *Neeraj Singh* - -* Remove deprecated `SchemaStatements#distinct`. - - *Francesco Rodriguez* - -* Move deprecated `ActiveRecord::TestCase` into the rails test - suite. The class is no longer public and is only used for internal - Rails tests. - - *Yves Senn* - -* Removed support for deprecated option `:restrict` for `:dependent` - in associations. - - *Neeraj Singh* - -* Removed support for deprecated `delete_sql` in associations. - - *Neeraj Singh* - -* Removed support for deprecated `insert_sql` in associations. - - *Neeraj Singh* - -* Removed support for deprecated `finder_sql` in associations. - - *Neeraj Singh* - -* Support array as root element in JSON fields. - - *Alexey Noskov & Francesco Rodriguez* - -* Removed support for deprecated `counter_sql` in associations. - - *Neeraj Singh* - -* Do not invoke callbacks when `delete_all` is called on collection. - - Method `delete_all` should not be invoking callbacks and this - feature was deprecated in Rails 4.0. This is being removed. - `delete_all` will continue to honor the `:dependent` option. However - if `:dependent` value is `:destroy` then the `:delete_all` deletion - strategy for that collection will be applied. - - User can also force a deletion strategy by passing parameter to - `delete_all`. For example you can do `@post.comments.delete_all(:nullify)`. - - *Neeraj Singh* - -* Calling default_scope without a proc will now raise `ArgumentError`. - - *Neeraj Singh* - -* Removed deprecated method `type_cast_code` from Column. - - *Neeraj Singh* - -* Removed deprecated options `delete_sql` and `insert_sql` from HABTM - association. - - Removed deprecated options `finder_sql` and `counter_sql` from - collection association. - - *Neeraj Singh* - -* Remove deprecated `ActiveRecord::Base#connection` method. - Make sure to access it via the class. - - *Yves Senn* - -* Remove deprecation warning for `auto_explain_threshold_in_seconds`. - - *Yves Senn* - -* Remove deprecated `:distinct` option from `Relation#count`. - - *Yves Senn* - -* Removed deprecated methods `partial_updates`, `partial_updates?` and - `partial_updates=`. - - *Neeraj Singh* - -* Removed deprecated method `scoped`. - - *Neeraj Singh* - -* Removed deprecated method `default_scopes?`. - - *Neeraj Singh* - -* Remove implicit join references that were deprecated in 4.0. - - Example: - - # before with implicit joins - Comment.where('posts.author_id' => 7) - - # after - Comment.references(:posts).where('posts.author_id' => 7) - - *Yves Senn* - -* Apply default scope when joining associations. For example: - - class Post < ActiveRecord::Base - default_scope -> { where published: true } - end - - class Comment - belongs_to :post - end - - When calling `Comment.joins(:post)`, we expect to receive only - comments on published posts, since that is the default scope for - posts. - - Before this change, the default scope from `Post` was not applied, - so we'd get comments on unpublished posts. - - *Jon Leighton* - -* Remove `activerecord-deprecated_finders` as a dependency. - - *Łukasz Strzałkowski* - -* Remove Oracle / Sqlserver / Firebird database tasks that were deprecated in 4.0. - - *kennyj* - -* `find_each` now returns an `Enumerator` when called without a block, so that it - can be chained with other `Enumerable` methods. - - *Ben Woosley* - -* `ActiveRecord::Result.each` now returns an `Enumerator` when called without - a block, so that it can be chained with other `Enumerable` methods. - - *Ben Woosley* - -* Flatten merged join_values before building the joins. - - While joining_values special treatment is given to string values. - By flattening the array it ensures that string values are detected - as strings and not arrays. - - Fixes #10669. - - *Neeraj Singh and iwiznia* - -* Do not load all child records for inverse case. - - currently `post.comments.find(Comment.first.id)` would load all - comments for the given post to set the inverse association. - - This has a huge performance penalty. Because if post has 100k - records and all these 100k records would be loaded in memory - even though the comment id was supplied. - - Fix is to use in-memory records only if loaded? is true. Otherwise - load the records using full sql. - - Fixes #10509. - - *Neeraj Singh* - -* `inspect` on Active Record model classes does not initiate a - new connection. This means that calling `inspect`, when the - database is missing, will no longer raise an exception. - Fixes #10936. +* Coerce strings when reading attributes. + Fixes #10485. Example: - Author.inspect # => "Author(no database connection)" + book = Book.new(title: 12345) + book.save! + book.title # => "12345" *Yves Senn* -* Handle single quotes in PostgreSQL default column values. - Fixes #10881. - - *Dylan Markow* - -* Log the sql that is actually sent to the database. - - If I have a query that produces sql - `WHERE "users"."name" = 'a b'` then in the log all the - whitespace is being squeezed. So the sql that is printed in the - log is `WHERE "users"."name" = 'a b'`. - - Do not squeeze whitespace out of sql queries. Fixes #10982. - - *Neeraj Singh* +* Deprecate half-baked support for PostgreSQL range values with excluding beginnings. + We currently map PostgreSQL ranges to Ruby ranges. This conversion is not fully + possible because the Ruby range does not support excluded beginnings. -* Fixture setup no longer depends on `ActiveRecord::Base.configurations`. - This is relevant when `ENV["DATABASE_URL"]` is used in place of a `database.yml`. + The current solution of incrementing the beginning is not correct and is now + deprecated. For subtypes where we don't know how to increment (e.g. `#succ` + is not defined) it will raise an ArgumentException for ranges with excluding + beginnings. *Yves Senn* -* Fix mysql2 adapter raises the correct exception when executing a query on a - closed connection. - - *Yves Senn* - -* Ambiguous reflections are on :through relationships are no longer supported. - For example, you need to change this: - - class Author < ActiveRecord::Base - has_many :posts - has_many :taggings, through: :posts - end - - class Post < ActiveRecord::Base - has_one :tagging - has_many :taggings - end - - class Tagging < ActiveRecord::Base - end - - To this: - - class Author < ActiveRecord::Base - has_many :posts - has_many :taggings, through: :posts, source: :tagging - end - - class Post < ActiveRecord::Base - has_one :tagging - has_many :taggings - end - - class Tagging < ActiveRecord::Base - end - - *Aaron Patterson* - -* Remove column restrictions for `count`, let the database raise if the SQL is - invalid. The previous behavior was untested and surprising for the user. - Fixes #5554. - - Example: - - User.select("name, username").count - # Before => SELECT count(*) FROM users - # After => ActiveRecord::StatementInvalid - - # you can still use `count(:all)` to perform a query unrelated to the - # selected columns - User.select("name, username").count(:all) # => SELECT count(*) FROM users +* Support for user created range types in PostgreSQL. *Yves Senn* -* Rails now automatically detects inverse associations. If you do not set the - `:inverse_of` option on the association, then Active Record will guess the - inverse association based on heuristics. - - Note that automatic inverse detection only works on `has_many`, `has_one`, - and `belongs_to` associations. Extra options on the associations will - also prevent the association's inverse from being found automatically. - - The automatic guessing of the inverse association uses a heuristic based - on the name of the class, so it may not work for all associations, - especially the ones with non-standard names. - - You can turn off the automatic detection of inverse associations by setting - the `:inverse_of` option to `false` like so: - - class Taggable < ActiveRecord::Base - belongs_to :tag, inverse_of: false - end - - *John Wang* - -* Fix `add_column` with `array` option when using PostgreSQL. Fixes #10432. - - *Adam Anderson* - -* Usage of `implicit_readonly` is being removed`. Please use `readonly` method - explicitly to mark records as `readonly. - Fixes #10615. - - Example: - - user = User.joins(:todos).select("users.*, todos.title as todos_title").readonly(true).first - user.todos_title = 'clean pet' - user.save! # will raise error - - *Yves Senn* - -* Fix the `:primary_key` option for `has_many` associations. - - Fixes #10693. - - *Yves Senn* - -* Fix bug where tiny types are incorrectly coerced as boolean when the length is more than 1. - - Fixes #10620. - - *Aaron Patterson* - -* Also support extensions in PostgreSQL 9.1. This feature has been supported since 9.1. - - *kennyj* - -* Deprecate `ConnectionAdapters::SchemaStatements#distinct`, - as it is no longer used by internals. - - *Ben Woosley* - -* Fix pending migrations error when loading schema and `ActiveRecord::Base.table_name_prefix` - is not blank. - - Call `assume_migrated_upto_version` on connection to prevent it from first - being picked up in `method_missing`. - - In the base class, `Migration`, `method_missing` expects the argument to be a - table name, and calls `proper_table_name` on the arguments before sending to - `connection`. If `table_name_prefix` or `table_name_suffix` is used, the schema - version changes to `prefix_version_suffix`, breaking `rake test:prepare`. - - Fixes #10411. - - *Kyle Stevens* - -* Method `read_attribute_before_type_cast` should accept input as symbol. - - *Neeraj Singh* - -* Confirm a record has not already been destroyed before decrementing counter cache. - - *Ben Tucker* - -* Fixed a bug in `ActiveRecord#sanitize_sql_hash_for_conditions` in which - `self.class` is an argument to `PredicateBuilder#build_from_hash` - causing `PredicateBuilder` to call non-existent method - `Class#reflect_on_association`. - - *Zach Ohlgren* - -* While removing index if column option is missing then raise IrreversibleMigration exception. - - Following code should raise `IrreversibleMigration`. But the code was - failing since options is an array and not a hash. - - def change - change_table :users do |t| - t.remove_index [:name, :email] - end - end - - Fix was to check if the options is a Hash before operating on it. - - Fixes #10419. - - *Neeraj Singh* - -* Do not overwrite manually built records during one-to-one nested attribute assignment - - For one-to-one nested associations, if you build the new (in-memory) - child object yourself before assignment, then the NestedAttributes - module will not overwrite it, e.g.: - - class Member < ActiveRecord::Base - has_one :avatar - accepts_nested_attributes_for :avatar - - def avatar - super || build_avatar(width: 200) - end - end - - member = Member.new - member.avatar_attributes = {icon: 'sad'} - member.avatar.width # => 200 - - *Olek Janiszewski* - -* fixes bug introduced by #3329. Now, when autosaving associations, - deletions happen before inserts and saves. This prevents a 'duplicate - unique value' database error that would occur if a record being created had - the same value on a unique indexed field as that of a record being destroyed. - - *Johnny Holton* - -* Handle aliased attributes in ActiveRecord::Relation. - - When using symbol keys, ActiveRecord will now translate aliased attribute names to the actual column name used in the database: - - With the model - - class Topic - alias_attribute :heading, :title - end - - The call - - Topic.where(heading: 'The First Topic') - - should yield the same result as - - Topic.where(title: 'The First Topic') - - This also applies to ActiveRecord::Relation::Calculations calls such as `Model.sum(:aliased)` and `Model.pluck(:aliased)`. - - This will not work with SQL fragment strings like `Model.sum('DISTINCT aliased')`. - - *Godfrey Chan* - -* Mute `psql` output when running rake db:schema:load. - - *Godfrey Chan* - -* Trigger a save on `has_one association=(associate)` when the associate contents have changed. - - Fixes #8856. - - *Chris Thompson* - -* Abort a rake task when missing db/structure.sql like `db:schema:load` task. - - *kennyj* - -* rake:db:test:prepare falls back to original environment after execution. - - *Slava Markevich* - -Please check [4-0-stable](https://github.com/rails/rails/blob/4-0-stable/activerecord/CHANGELOG.md) for previous changes. +Please check [4-1-stable](https://github.com/rails/rails/blob/4-1-stable/activerecord/CHANGELOG.md) for previous changes. diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 142d21ce92..53f7591226 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1036,6 +1036,9 @@ module ActiveRecord # Specifies a one-to-many association. The following methods for retrieval and query of # collections of associated objects will be added: # + # +collection+ is a placeholder for the symbol passed as the first argument, so + # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>. + # # [collection(force_reload = false)] # Returns an array of all the associated objects. # An empty array is returned if none are found. @@ -1094,9 +1097,6 @@ module ActiveRecord # Does the same as <tt>collection.create</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt> # if the record is invalid. # - # (*Note*: +collection+ is replaced with the symbol passed as the first argument, so - # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.) - # # === Example # # A <tt>Firm</tt> class declares <tt>has_many :clients</tt>, which will add: @@ -1211,6 +1211,9 @@ module ActiveRecord # # The following methods for retrieval and query of a single associated object will be added: # + # +association+ is a placeholder for the symbol passed as the first argument, so + # <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>. + # # [association(force_reload = false)] # Returns the associated object. +nil+ is returned if none is found. # [association=(associate)] @@ -1229,9 +1232,6 @@ module ActiveRecord # Does the same as <tt>create_association</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt> # if the record is invalid. # - # (+association+ is replaced with the symbol passed as the first argument, so - # <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.) - # # === Example # # An Account class declares <tt>has_one :beneficiary</tt>, which will add: @@ -1317,6 +1317,9 @@ module ActiveRecord # Methods will be added for retrieval and query for a single associated object, for which # this object holds an id: # + # +association+ is a placeholder for the symbol passed as the first argument, so + # <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>. + # # [association(force_reload = false)] # Returns the associated object. +nil+ is returned if none is found. # [association=(associate)] @@ -1332,9 +1335,6 @@ module ActiveRecord # Does the same as <tt>create_association</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt> # if the record is invalid. # - # (+association+ is replaced with the symbol passed as the first argument, so - # <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.) - # # === Example # # A Post class declares <tt>belongs_to :author</tt>, which will add: @@ -1454,6 +1454,9 @@ module ActiveRecord # # Adds the following methods for retrieval and query: # + # +collection+ is a placeholder for the symbol passed as the first argument, so + # <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>. + # # [collection(force_reload = false)] # Returns an array of all the associated objects. # An empty array is returned if none are found. @@ -1495,9 +1498,6 @@ module ActiveRecord # with +attributes+, linked to this object through the join table, and that has already been # saved (if it passed the validation). # - # (+collection+ is replaced with the symbol passed as the first argument, so - # <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.) - # # === Example # # A Developer class declares <tt>has_and_belongs_to_many :projects</tt>, which will add: diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb index 4e46256862..9ad2d2fb12 100644 --- a/activerecord/lib/active_record/associations/association.rb +++ b/activerecord/lib/active_record/associations/association.rb @@ -232,7 +232,7 @@ module ActiveRecord # Returns true if record contains the foreign_key def foreign_key_for?(record) - record.attributes.has_key? reflection.foreign_key + record.has_attribute?(reflection.foreign_key) end # This should be implemented to return the values of the relevant key(s) on the owner, diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 03ca00fa70..270871c866 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -24,10 +24,6 @@ module ActiveRecord # If you need to work on all current children, new and existing records, # +load_target+ and the +loaded+ flag are your friends. class CollectionAssociation < Association #:nodoc: - def initialize(owner, reflection) - super - @proxy = CollectionProxy.create(klass, self) - end # Implements the reader method, e.g. foo.items for Foo.has_many :items def reader(force_reload = false) @@ -37,7 +33,7 @@ module ActiveRecord reload end - @proxy + @proxy ||= CollectionProxy.create(klass, self) end # Implements the writer method, e.g. foo.items= for Foo.has_many :items @@ -517,13 +513,13 @@ module ActiveRecord target end - def concat_records(records) + def concat_records(records, should_raise = false) result = true records.flatten.each do |record| raise_on_type_mismatch!(record) add_to_target(record) do |rec| - result &&= insert_record(rec) unless owner.new_record? + result &&= insert_record(rec, true, should_raise) unless owner.new_record? end end diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 31b8d27892..64bc98c642 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -30,7 +30,6 @@ module ActiveRecord unless owner.new_record? records.flatten.each do |record| raise_on_type_mismatch!(record) - record.save! if record.new_record? end end @@ -40,7 +39,7 @@ module ActiveRecord def concat_records(records) ensure_not_nested - records = super + records = super(records, true) if owner.new_record? && records records.flatten.each do |record| diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 9326c9c117..57ceec2fc5 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -105,8 +105,9 @@ module ActiveRecord super else # If B < A and A defines its own attribute method, then we don't want to overwrite that. - defined = method_defined_within?(method_name, superclass, superclass.generated_attribute_methods) - defined && !ActiveRecord::Base.method_defined?(method_name) || super + defined = method_defined_within?(method_name, superclass) && + superclass.instance_method(method_name).owner != superclass.generated_attribute_methods + defined || super end end diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 4f58d06f35..e9622ca0c1 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -301,7 +301,7 @@ module ActiveRecord def association_valid?(reflection, record) return true if record.destroyed? || record.marked_for_destruction? - unless valid = record.valid?(self.validation_context) + unless valid = record.valid? if reflection.options[:autosave] record.errors.each do |attribute, message| attribute = "#{reflection.name}.#{attribute}" diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 759e162e19..fd185de589 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -538,7 +538,10 @@ module ActiveRecord # for (not necessarily the current class). def retrieve_connection(klass) #:nodoc: pool = retrieve_connection_pool(klass) - (pool && pool.connection) or raise ConnectionNotEstablished + raise ConnectionNotEstablished, "No connection pool for #{klass}" unless pool + conn = pool.connection + raise ConnectionNotEstablished, "No connection for #{klass} in connection pool" unless conn + conn end # Returns true if a connection that's accessible to this class has diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index ad069f5e53..7f530ec5e4 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -40,7 +40,7 @@ module ActiveRecord # index_exists?(:suppliers, :company_id, unique: true) # # # Check an index with a custom name exists - # index_exists?(:suppliers, :company_id, name: "idx_company_id" + # index_exists?(:suppliers, :company_id, name: "idx_company_id") # def index_exists?(table_name, column_name, options = {}) column_names = Array(column_name) diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index f2fbd5a8f2..187eefb9e4 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -87,7 +87,7 @@ module ActiveRecord end end - # Casts value (which is a String) to an appropriate instance. + # Casts value to an appropriate instance. def type_cast(value) return nil if value.nil? return coder.load(value) if encoded? @@ -95,7 +95,13 @@ module ActiveRecord klass = self.class case type - when :string, :text then value + when :string, :text + case value + when TrueClass; "1" + when FalseClass; "0" + else + value.to_s + end when :integer then klass.value_to_integer(value) when :float then value.to_f when :decimal then klass.value_to_decimal(value) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb index fae260a921..e7df073627 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb @@ -6,6 +6,10 @@ module ActiveRecord module OID class Type def type; end + + def infinity(options = {}) + ::Float::INFINITY * (options[:negative] ? -1 : 1) + end end class Identity < Type @@ -14,6 +18,14 @@ module ActiveRecord end end + class Text < Type + def type_cast(value) + return if value.nil? + + value.to_s + end + end + class Bit < Type def type_cast(value) if String === value @@ -109,23 +121,19 @@ module ActiveRecord def extract_bounds(value) from, to = value[1..-2].split(',') { - from: (value[1] == ',' || from == '-infinity') ? infinity(:negative => true) : from, - to: (value[-2] == ',' || to == 'infinity') ? infinity : to, + from: (value[1] == ',' || from == '-infinity') ? @subtype.infinity(negative: true) : from, + to: (value[-2] == ',' || to == 'infinity') ? @subtype.infinity : to, exclude_start: (value[0] == '('), exclude_end: (value[-1] == ')') } end - def infinity(options = {}) - ::Float::INFINITY * (options[:negative] ? -1 : 1) - end - def infinity?(value) value.respond_to?(:infinite?) && value.infinite? end - def to_integer(value) - infinity?(value) ? value : value.to_i + def type_cast_single(value) + infinity?(value) ? value : @subtype.type_cast(value) end def type_cast(value) @@ -133,27 +141,20 @@ module ActiveRecord return value if value.is_a?(::Range) extracted = extract_bounds(value) - - case @subtype - when :date - from = ConnectionAdapters::Column.value_to_date(extracted[:from]) - from -= 1.day if extracted[:exclude_start] - to = ConnectionAdapters::Column.value_to_date(extracted[:to]) - when :decimal - from = BigDecimal.new(extracted[:from].to_s) - # FIXME: add exclude start for ::Range, same for timestamp ranges - to = BigDecimal.new(extracted[:to].to_s) - when :time - from = ConnectionAdapters::Column.string_to_time(extracted[:from]) - to = ConnectionAdapters::Column.string_to_time(extracted[:to]) - when :integer - from = to_integer(extracted[:from]) rescue value ? 1 : 0 - from -= 1 if extracted[:exclude_start] - to = to_integer(extracted[:to]) rescue value ? 1 : 0 - else - return value + from = type_cast_single extracted[:from] + to = type_cast_single extracted[:to] + + if !infinity?(from) && extracted[:exclude_start] + if from.respond_to?(:succ) + from = from.succ + ActiveSupport::Deprecation.warn <<-MESSAGE +Excluding the beginning of a Range is only partialy supported through `#succ`. +This is not reliable and will be removed in the future. + MESSAGE + else + raise ArgumentError, "The Ruby Range object does not support excluding the beginning of a Range. (unsupported value: '#{value}')" + end end - ::Range.new(from, to, extracted[:exclude_end]) end end @@ -222,6 +223,10 @@ module ActiveRecord ConnectionAdapters::Column.value_to_decimal value end + + def infinity(options = {}) + BigDecimal.new("Infinity") * (options[:negative] ? -1 : 1) + end end class Hstore < Type @@ -331,15 +336,8 @@ module ActiveRecord alias_type 'int8', 'int2' alias_type 'oid', 'int2' - register_type 'daterange', OID::Range.new(:date) - register_type 'numrange', OID::Range.new(:decimal) - register_type 'tsrange', OID::Range.new(:time) - register_type 'int4range', OID::Range.new(:integer) - alias_type 'tstzrange', 'tsrange' - alias_type 'int8range', 'int4range' - register_type 'numeric', OID::Decimal.new - register_type 'text', OID::Identity.new + register_type 'text', OID::Text.new alias_type 'varchar', 'text' alias_type 'char', 'text' alias_type 'bpchar', 'text' @@ -365,13 +363,13 @@ module ActiveRecord register_type 'date', OID::Date.new register_type 'time', OID::Time.new - register_type 'path', OID::Identity.new + register_type 'path', OID::Text.new register_type 'point', OID::Point.new - register_type 'polygon', OID::Identity.new - register_type 'circle', OID::Identity.new + register_type 'polygon', OID::Text.new + register_type 'circle', OID::Text.new register_type 'hstore', OID::Hstore.new register_type 'json', OID::Json.new - register_type 'ltree', OID::Identity.new + register_type 'ltree', OID::Text.new register_type 'cidr', OID::Cidr.new alias_type 'inet', 'cidr' diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 36c7462419..d1fb132b60 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -785,18 +785,29 @@ module ActiveRecord end def initialize_type_map(type_map) - result = execute('SELECT oid, typname, typelem, typdelim, typinput FROM pg_type', 'SCHEMA') - leaves, nodes = result.partition { |row| row['typelem'] == '0' } + if supports_ranges? + result = execute(<<-SQL, 'SCHEMA') + SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype + FROM pg_type as t + LEFT JOIN pg_range as r ON oid = rngtypid + SQL + else + result = execute(<<-SQL, 'SCHEMA') + SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput + FROM pg_type as t + SQL + end + ranges, nodes = result.partition { |row| row['typinput'] == 'range_in' } + leaves, nodes = nodes.partition { |row| row['typelem'] == '0' } + arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' } - # populate the leaf nodes + # populate the base types leaves.find_all { |row| OID.registered_type? row['typname'] }.each do |row| type_map[row['oid'].to_i] = OID::NAMES[row['typname']] end records_by_oid = result.group_by { |row| row['oid'] } - arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' } - # populate composite types nodes.each do |row| add_oid row, records_by_oid, type_map @@ -807,6 +818,13 @@ module ActiveRecord array = OID::Array.new type_map[row['typelem'].to_i] type_map[row['oid'].to_i] = array end + + # populate range types + ranges.find_all { |row| type_map.key? row['rngsubtype'].to_i }.each do |row| + subtype = type_map[row['rngsubtype'].to_i] + range = OID::Range.new type_map[row['rngsubtype'].to_i] + type_map[row['oid'].to_i] = range + end end FEATURE_NOT_SUPPORTED = "0A000" #:nodoc: diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb index 11f6a47158..4ba4e09777 100644 --- a/activerecord/lib/active_record/connection_handling.rb +++ b/activerecord/lib/active_record/connection_handling.rb @@ -93,16 +93,12 @@ module ActiveRecord # the connection URL. This hash responds to any string key with # resolved connection information. def default_url_hash - if @raw_config.blank? - Hash.new do |hash, key| - hash[key] = if key.is_a? String - ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(@url).to_hash - else - nil - end + Hash.new do |hash, key| + hash[key] = if key.is_a? String + ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(@url).to_hash + else + nil end - else - {} end end end diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 297792aeec..59467636d7 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -549,7 +549,7 @@ module ActiveRecord end # Returns a consistent, platform-independent identifier for +label+. - # Identifiers are positive integers less than 2^32. + # Identifiers are positive integers less than 2^30. def self.identify(label) Zlib.crc32(label.to_s) % MAX_ID end diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index 7795561e51..5eb7f7e205 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -1,7 +1,7 @@ module ActiveRecord # Returns the version of the currently loaded ActiveRecord as a Gem::Version def self.version - Gem::Version.new "4.1.0.beta2" + Gem::Version.new "4.2.0.alpha" end module VERSION #:nodoc: diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb index 5c3a797c41..158bc6ee89 100644 --- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb +++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb @@ -27,9 +27,6 @@ end class PostgresqlTimestampWithZone < ActiveRecord::Base end -class PostgresqlUUID < ActiveRecord::Base -end - class PostgresqlLtree < ActiveRecord::Base end @@ -68,14 +65,11 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase @first_oid = PostgresqlOid.find(1) @connection.execute("INSERT INTO postgresql_timestamp_with_zones (id, time) VALUES (1, '2010-01-01 10:00:00-1')") - - @connection.execute("INSERT INTO postgresql_uuids (id, guid, compact_guid) VALUES(1, 'd96c3da0-96c1-012f-1316-64ce8f32c6d8', 'f06c715096c1012f131764ce8f32c6d8')") - @first_uuid = PostgresqlUUID.find(1) end def teardown [PostgresqlArray, PostgresqlTsvector, PostgresqlMoney, PostgresqlNumber, PostgresqlTime, PostgresqlNetworkAddress, - PostgresqlBitString, PostgresqlOid, PostgresqlTimestampWithZone, PostgresqlUUID].each(&:delete_all) + PostgresqlBitString, PostgresqlOid, PostgresqlTimestampWithZone].each(&:delete_all) end def test_array_escaping @@ -124,10 +118,6 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase assert_equal :integer, @first_oid.column_for_attribute(:obj_id).type end - def test_data_type_of_uuid_types - assert_equal :uuid, @first_uuid.column_for_attribute(:guid).type - end - def test_array_values assert_equal [35000,21000,18000,17000], @first_array.commission_by_quarter assert_equal ['foo','bar','baz'], @first_array.nicknames @@ -180,11 +170,6 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase assert_equal '01:23:45:67:89:0a', @first_network_address.mac_address end - def test_uuid_values - assert_equal 'd96c3da0-96c1-012f-1316-64ce8f32c6d8', @first_uuid.guid - assert_equal 'f06c7150-96c1-012f-1317-64ce8f32c6d8', @first_uuid.compact_guid - end - def test_bit_string_values assert_equal '00010101', @first_bit_string.bit_string assert_equal '00010101', @first_bit_string.bit_string_varying diff --git a/activerecord/test/cases/adapters/postgresql/enum_test.rb b/activerecord/test/cases/adapters/postgresql/enum_test.rb new file mode 100644 index 0000000000..bf8dbd6c3f --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/enum_test.rb @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +require "cases/helper" +require 'active_record/base' +require 'active_record/connection_adapters/postgresql_adapter' + +class PostgresqlEnumTest < ActiveRecord::TestCase + class PostgresqlEnum < ActiveRecord::Base + self.table_name = "postgresql_enums" + end + + def teardown + @connection.execute 'DROP TABLE IF EXISTS postgresql_enums' + @connection.execute 'DROP TYPE IF EXISTS mood' + end + + def setup + @connection = ActiveRecord::Base.connection + @connection.transaction do + @connection.execute <<-SQL + CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy'); + SQL + @connection.create_table('postgresql_enums') do |t| + t.column :current_mood, :mood + end + end + end + + def test_enum_mapping + @connection.execute "INSERT INTO postgresql_enums VALUES (1, 'sad');" + enum = PostgresqlEnum.first + assert_equal "sad", enum.current_mood + + enum.current_mood = "happy" + enum.save! + + assert_equal "happy", enum.reload.current_mood + end +end diff --git a/activerecord/test/cases/adapters/postgresql/range_test.rb b/activerecord/test/cases/adapters/postgresql/range_test.rb index 5c2d8e1c1d..306bb09a0d 100644 --- a/activerecord/test/cases/adapters/postgresql/range_test.rb +++ b/activerecord/test/cases/adapters/postgresql/range_test.rb @@ -10,12 +10,22 @@ if ActiveRecord::Base.connection.supports_ranges? class PostgresqlRangeTest < ActiveRecord::TestCase def teardown @connection.execute 'DROP TABLE IF EXISTS postgresql_ranges' + @connection.execute 'DROP TYPE IF EXISTS floatrange' end def setup - @connection = ActiveRecord::Base.connection + @connection = PostgresqlRange.connection begin @connection.transaction do + @connection.execute 'DROP TABLE IF EXISTS postgresql_ranges' + @connection.execute 'DROP TYPE IF EXISTS floatrange' + @connection.execute <<_SQL + CREATE TYPE floatrange AS RANGE ( + subtype = float8, + subtype_diff = float8mi + ); +_SQL + @connection.create_table('postgresql_ranges') do |t| t.daterange :date_range t.numrange :num_range @@ -24,7 +34,11 @@ if ActiveRecord::Base.connection.supports_ranges? t.int4range :int4_range t.int8range :int8_range end + + @connection.add_column 'postgresql_ranges', 'float_range', 'floatrange' end + @connection.send :reload_type_map + PostgresqlRange.reset_column_information rescue ActiveRecord::StatementInvalid skip "do not test on PG without range" end @@ -35,23 +49,26 @@ if ActiveRecord::Base.connection.supports_ranges? ts_range: "[''2010-01-01 14:30'', ''2011-01-01 14:30'']", tstz_range: "[''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'']", int4_range: "[1, 10]", - int8_range: "[10, 100]") + int8_range: "[10, 100]", + float_range: "[0.5, 0.7]") insert_range(id: 102, - date_range: "(''2012-01-02'', ''2012-01-04'')", + date_range: "[''2012-01-02'', ''2012-01-04'')", num_range: "[0.1, 0.2)", ts_range: "[''2010-01-01 14:30'', ''2011-01-01 14:30'')", tstz_range: "[''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'')", - int4_range: "(1, 10)", - int8_range: "(10, 100)") + int4_range: "[1, 10)", + int8_range: "[10, 100)", + float_range: "[0.5, 0.7)") insert_range(id: 103, - date_range: "(''2012-01-02'',]", + date_range: "[''2012-01-02'',]", num_range: "[0.1,]", ts_range: "[''2010-01-01 14:30'',]", tstz_range: "[''2010-01-01 14:30:00+05'',]", - int4_range: "(1,]", - int8_range: "(10,]") + int4_range: "[1,]", + int8_range: "[10,]", + float_range: "[0.5,]") insert_range(id: 104, date_range: "[,]", @@ -59,15 +76,17 @@ if ActiveRecord::Base.connection.supports_ranges? ts_range: "[,]", tstz_range: "[,]", int4_range: "[,]", - int8_range: "[,]") + int8_range: "[,]", + float_range: "[,]") insert_range(id: 105, - date_range: "(''2012-01-02'', ''2012-01-02'')", - num_range: "(0.1, 0.1)", - ts_range: "(''2010-01-01 14:30'', ''2010-01-01 14:30'')", - tstz_range: "(''2010-01-01 14:30:00+05'', ''2010-01-01 06:30:00-03'')", - int4_range: "(1, 1)", - int8_range: "(10, 10)") + date_range: "[''2012-01-02'', ''2012-01-02'')", + num_range: "[0.1, 0.1)", + ts_range: "[''2010-01-01 14:30'', ''2010-01-01 14:30'')", + tstz_range: "[''2010-01-01 14:30:00+05'', ''2010-01-01 06:30:00-03'')", + int4_range: "[1, 1)", + int8_range: "[10, 10)", + float_range: "[0.5, 0.5)") @new_range = PostgresqlRange.new @first_range = PostgresqlRange.find(101) @@ -88,24 +107,24 @@ if ActiveRecord::Base.connection.supports_ranges? def test_int4range_values assert_equal 1...11, @first_range.int4_range - assert_equal 2...10, @second_range.int4_range - assert_equal 2...Float::INFINITY, @third_range.int4_range + assert_equal 1...10, @second_range.int4_range + assert_equal 1...Float::INFINITY, @third_range.int4_range assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.int4_range) assert_nil @empty_range.int4_range end def test_int8range_values assert_equal 10...101, @first_range.int8_range - assert_equal 11...100, @second_range.int8_range - assert_equal 11...Float::INFINITY, @third_range.int8_range + assert_equal 10...100, @second_range.int8_range + assert_equal 10...Float::INFINITY, @third_range.int8_range assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.int8_range) assert_nil @empty_range.int8_range end def test_daterange_values assert_equal Date.new(2012, 1, 2)...Date.new(2012, 1, 5), @first_range.date_range - assert_equal Date.new(2012, 1, 3)...Date.new(2012, 1, 4), @second_range.date_range - assert_equal Date.new(2012, 1, 3)...Float::INFINITY, @third_range.date_range + assert_equal Date.new(2012, 1, 2)...Date.new(2012, 1, 4), @second_range.date_range + assert_equal Date.new(2012, 1, 2)...Float::INFINITY, @third_range.date_range assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.date_range) assert_nil @empty_range.date_range end @@ -133,6 +152,14 @@ if ActiveRecord::Base.connection.supports_ranges? assert_nil @empty_range.tstz_range end + def test_custom_range_values + assert_equal 0.5..0.7, @first_range.float_range + assert_equal 0.5...0.7, @second_range.float_range + assert_equal 0.5...Float::INFINITY, @third_range.float_range + assert_equal -Float::INFINITY...Float::INFINITY, @fourth_range.float_range + assert_nil @empty_range.float_range + end + def test_create_tstzrange tstzrange = Time.parse('2010-01-01 14:30:00 +0100')...Time.parse('2011-02-02 14:30:00 CDT') round_trip(@new_range, :tstz_range, tstzrange) @@ -203,6 +230,38 @@ if ActiveRecord::Base.connection.supports_ranges? assert_nil_round_trip(@first_range, :int8_range, 39999...39999) end + def test_exclude_beginning_for_subtypes_with_succ_method_is_deprecated + tz = ::ActiveRecord::Base.default_timezone + + silence_warnings { + assert_deprecated { + range = PostgresqlRange.create!(date_range: "(''2012-01-02'', ''2012-01-04'']") + assert_equal Date.new(2012, 1, 3)..Date.new(2012, 1, 4), range.date_range + } + assert_deprecated { + range = PostgresqlRange.create!(ts_range: "(''2010-01-01 14:30'', ''2011-01-01 14:30'']") + assert_equal Time.send(tz, 2010, 1, 1, 14, 30, 1)..Time.send(tz, 2011, 1, 1, 14, 30, 0), range.ts_range + } + assert_deprecated { + range = PostgresqlRange.create!(tstz_range: "(''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'']") + assert_equal Time.parse('2010-01-01 09:30:01 UTC')..Time.parse('2011-01-01 17:30:00 UTC'), range.tstz_range + } + assert_deprecated { + range = PostgresqlRange.create!(int4_range: "(1, 10]") + assert_equal 2..10, range.int4_range + } + assert_deprecated { + range = PostgresqlRange.create!(int8_range: "(10, 100]") + assert_equal 11..100, range.int8_range + } + } + end + + def test_exclude_beginning_for_subtypes_without_succ_method_is_not_supported + assert_raises(ArgumentError) { PostgresqlRange.create!(num_range: "(0.1, 0.2]") } + assert_raises(ArgumentError) { PostgresqlRange.create!(float_range: "(0.5, 0.7]") } + end + private def assert_equal_round_trip(range, attribute, value) round_trip(range, attribute, value) @@ -229,7 +288,8 @@ if ActiveRecord::Base.connection.supports_ranges? ts_range, tstz_range, int4_range, - int8_range + int8_range, + float_range ) VALUES ( #{values[:id]}, '#{values[:date_range]}', @@ -237,7 +297,8 @@ if ActiveRecord::Base.connection.supports_ranges? '#{values[:ts_range]}', '#{values[:tstz_range]}', '#{values[:int4_range]}', - '#{values[:int8_range]}' + '#{values[:int8_range]}', + '#{values[:float_range]}' ) SQL end diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb index 3f5d981444..f581c4cf39 100644 --- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb +++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb @@ -4,31 +4,77 @@ require "cases/helper" require 'active_record/base' require 'active_record/connection_adapters/postgresql_adapter' +module PostgresqlUUIDHelper + def connection + @connection ||= ActiveRecord::Base.connection + end + + def enable_uuid_ossp + unless connection.extension_enabled?('uuid-ossp') + connection.enable_extension 'uuid-ossp' + connection.commit_db_transaction + end + + connection.reconnect! + end + + def drop_table(name) + connection.execute "drop table if exists #{name}" + end +end + class PostgresqlUUIDTest < ActiveRecord::TestCase - class UUID < ActiveRecord::Base - self.table_name = 'pg_uuids' + include PostgresqlUUIDHelper + + class UUIDType < ActiveRecord::Base + self.table_name = "uuid_data_type" end - def setup - @connection = ActiveRecord::Base.connection + setup do + connection.create_table "uuid_data_type" do |t| + t.uuid 'guid' + end + end + + teardown do + drop_table "uuid_data_type" + end + + def test_data_type_of_uuid_types + assert_equal :uuid, UUIDType.columns_hash["guid"].type + end - unless @connection.extension_enabled?('uuid-ossp') - @connection.enable_extension 'uuid-ossp' - @connection.commit_db_transaction + def test_uuid_formats + ["A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11", + "{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}", + "a0eebc999c0b4ef8bb6d6bb9bd380a11", + "a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11", + "{a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}"].each do |valid_uuid| + UUIDType.create(guid: valid_uuid) + uuid = UUIDType.last + assert_equal "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", uuid.guid end + end +end - @connection.reconnect! +class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase + include PostgresqlUUIDHelper - @connection.transaction do - @connection.create_table('pg_uuids', id: :uuid, default: 'uuid_generate_v1()') do |t| - t.string 'name' - t.uuid 'other_uuid', default: 'uuid_generate_v4()' - end + class UUID < ActiveRecord::Base + self.table_name = 'pg_uuids' + end + + setup do + enable_uuid_ossp + + connection.create_table('pg_uuids', id: :uuid, default: 'uuid_generate_v1()') do |t| + t.string 'name' + t.uuid 'other_uuid', default: 'uuid_generate_v4()' end end - def teardown - @connection.execute 'drop table if exists pg_uuids' + teardown do + drop_table "pg_uuids" end if ActiveRecord::Base.connection.supports_extensions? @@ -49,14 +95,14 @@ class PostgresqlUUIDTest < ActiveRecord::TestCase end def test_pk_and_sequence_for_uuid_primary_key - pk, seq = @connection.pk_and_sequence_for('pg_uuids') + pk, seq = connection.pk_and_sequence_for('pg_uuids') assert_equal 'id', pk assert_equal nil, seq end def test_schema_dumper_for_uuid_primary_key schema = StringIO.new - ActiveRecord::SchemaDumper.dump(@connection, schema) + ActiveRecord::SchemaDumper.dump(connection, schema) assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: "uuid_generate_v1\(\)"/, schema.string) assert_match(/t\.uuid "other_uuid", default: "uuid_generate_v4\(\)"/, schema.string) end @@ -64,34 +110,24 @@ class PostgresqlUUIDTest < ActiveRecord::TestCase end class PostgresqlUUIDTestNilDefault < ActiveRecord::TestCase - class UUID < ActiveRecord::Base - self.table_name = 'pg_uuids' - end + include PostgresqlUUIDHelper - def setup - @connection = ActiveRecord::Base.connection - @connection.reconnect! - - unless @connection.extension_enabled?('uuid-ossp') - @connection.enable_extension 'uuid-ossp' - @connection.commit_db_transaction - end + setup do + enable_uuid_ossp - @connection.transaction do - @connection.create_table('pg_uuids', id: false) do |t| - t.primary_key :id, :uuid, default: nil - t.string 'name' - end + connection.create_table('pg_uuids', id: false) do |t| + t.primary_key :id, :uuid, default: nil + t.string 'name' end end - def teardown - @connection.execute 'drop table if exists pg_uuids' + teardown do + drop_table "pg_uuids" end if ActiveRecord::Base.connection.supports_extensions? def test_id_allows_default_override_via_nil - col_desc = @connection.execute("SELECT pg_get_expr(d.adbin, d.adrelid) as default + col_desc = connection.execute("SELECT pg_get_expr(d.adbin, d.adrelid) as default FROM pg_attribute a LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum WHERE a.attname='id' AND a.attrelid = 'pg_uuids'::regclass").first @@ -101,6 +137,8 @@ class PostgresqlUUIDTestNilDefault < ActiveRecord::TestCase end class PostgresqlUUIDTestInverseOf < ActiveRecord::TestCase + include PostgresqlUUIDHelper + class UuidPost < ActiveRecord::Base self.table_name = 'pg_uuid_posts' has_many :uuid_comments, inverse_of: :uuid_post @@ -111,30 +149,24 @@ class PostgresqlUUIDTestInverseOf < ActiveRecord::TestCase belongs_to :uuid_post end - def setup - @connection = ActiveRecord::Base.connection - @connection.reconnect! - - unless @connection.extension_enabled?('uuid-ossp') - @connection.enable_extension 'uuid-ossp' - @connection.commit_db_transaction - end + setup do + enable_uuid_ossp - @connection.transaction do - @connection.create_table('pg_uuid_posts', id: :uuid) do |t| + connection.transaction do + connection.create_table('pg_uuid_posts', id: :uuid) do |t| t.string 'title' end - @connection.create_table('pg_uuid_comments', id: :uuid) do |t| + connection.create_table('pg_uuid_comments', id: :uuid) do |t| t.uuid :uuid_post_id, default: 'uuid_generate_v4()' t.string 'content' end end end - def teardown - @connection.transaction do - @connection.execute 'drop table if exists pg_uuid_comments' - @connection.execute 'drop table if exists pg_uuid_posts' + teardown do + connection.transaction do + drop_table "pg_uuid_comments" + drop_table "pg_uuid_posts" end end diff --git a/activerecord/test/cases/associations/callbacks_test.rb b/activerecord/test/cases/associations/callbacks_test.rb index 2d0d4541b4..e555c52281 100644 --- a/activerecord/test/cases/associations/callbacks_test.rb +++ b/activerecord/test/cases/associations/callbacks_test.rb @@ -101,6 +101,27 @@ class AssociationCallbacksTest < ActiveRecord::TestCase "after_adding#{david.id}"], ar.developers_log end + def test_has_and_belongs_to_many_before_add_called_before_save + dev = nil + new_dev = nil + klass = Class.new(Project) do + def self.name; Project.name; end + has_and_belongs_to_many :developers_with_callbacks, + :class_name => "Developer", + :before_add => lambda { |o,r| + dev = r + new_dev = r.new_record? + } + end + rec = klass.create! + alice = Developer.new(:name => 'alice') + rec.developers_with_callbacks << alice + assert_equal alice, dev + assert_not_nil new_dev + assert new_dev, "record should not have been saved" + assert_not alice.new_record? + end + def test_has_and_belongs_to_many_after_add_called_after_save ar = projects(:active_record) assert ar.developers_log.empty? diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 321440cab7..a86fb15719 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -22,8 +22,6 @@ require 'models/engine' require 'models/categorization' require 'models/minivan' require 'models/speedometer' -require 'models/pirate' -require 'models/ship' class HasManyAssociationsTestForReorderWithJoinDependency < ActiveRecord::TestCase fixtures :authors, :posts, :comments @@ -1832,12 +1830,4 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end end end - - test 'has_many_association passes context validation to validate children' do - pirate = FamousPirate.new - pirate.famous_ships << ship = FamousShip.new - assert_equal true, pirate.valid? - assert_equal false, pirate.valid?(:conference) - assert_equal "can't be blank", ship.errors[:name].first - end end diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 47592f312e..026a7fe635 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -817,6 +817,13 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert author.named_categories(true).include?(category) end + def test_collection_exists + author = authors(:mary) + category = Category.create!(author_ids: [author.id], name: "Primary") + assert category.authors.exists?(id: author.id) + assert category.reload.authors.exists?(id: author.id) + end + def test_collection_delete_with_nonstandard_primary_key_on_belongs_to author = authors(:mary) category = author.named_categories.create(:name => "Primary") diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 6c581a432f..1a1e442df0 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -555,6 +555,24 @@ class AttributeMethodsTest < ActiveRecord::TestCase end end + def test_converted_values_are_returned_after_assignment + developer = Developer.new(name: 1337, salary: "50000") + + assert_equal "50000", developer.salary_before_type_cast + assert_equal 1337, developer.name_before_type_cast + + assert_equal 50000, developer.salary + assert_equal "1337", developer.name + + developer.save! + + assert_equal "50000", developer.salary_before_type_cast + assert_equal 1337, developer.name_before_type_cast + + assert_equal 50000, developer.salary + assert_equal "1337", developer.name + end + def test_write_nil_to_time_attributes in_time_zone "Pacific Time (US & Canada)" do record = @target.new @@ -728,19 +746,27 @@ class AttributeMethodsTest < ActiveRecord::TestCase assert "unknown attribute: hello", error.message end - def test_read_attribute_overwrites_private_method_not_considered_implemented - # simulate a model with a db column that shares its name an inherited - # private method (e.g. Object#system) - # - Object.class_eval do - private - def title; "private!"; end + def test_global_methods_are_overwritten + klass = Class.new(ActiveRecord::Base) do + self.table_name = 'computers' + end + + assert !klass.instance_method_already_implemented?(:system) + computer = klass.new + assert_nil computer.system + end + + def test_global_methods_are_overwritte_when_subclassing + klass = Class.new(ActiveRecord::Base) { self.abstract_class = true } + + subklass = Class.new(klass) do + self.table_name = 'computers' end - assert !@target.instance_method_already_implemented?(:title) - topic = @target.new - assert_nil topic.title - Object.send(:undef_method, :title) # remove test method from object + assert !klass.instance_method_already_implemented?(:system) + assert !subklass.instance_method_already_implemented?(:system) + computer = subklass.new + assert_nil computer.system end def test_instance_method_should_be_defined_on_the_base_class diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index c55dd685a1..d2f97df0fc 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -21,6 +21,31 @@ require 'models/electron' require 'models/molecule' class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase + def test_autosave_validation + person = Class.new(ActiveRecord::Base) { + self.table_name = 'people' + validate :should_be_cool, :on => :create + def self.name; 'Person'; end + + private + + def should_be_cool + unless self.first_name == 'cool' + errors.add :first_name, "not cool" + end + end + } + reference = Class.new(ActiveRecord::Base) { + self.table_name = "references" + def self.name; 'Reference'; end + belongs_to :person, autosave: true, class: person + } + + u = person.create!(first_name: 'cool') + u.update_attributes!(first_name: 'nah') # still valid because validation only applies on 'create' + assert reference.create!(person: u).persisted? + end + def test_should_not_add_the_same_callbacks_multiple_times_for_has_one assert_no_difference_when_adding_callbacks_twice_for Pirate, :ship end diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb index 318cc5a32c..599e8c762c 100644 --- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb +++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb @@ -17,6 +17,14 @@ module ActiveRecord ENV["DATABASE_URL"] = @previous_database_url end + def test_environment_does_not_exist_in_config_url_does_exist + ENV['DATABASE_URL'] = "postgres://localhost/foo" + config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } } + actual = klass.new(config).resolve + expect_prod = { "adapter"=>"postgresql", "database"=>"foo", "host"=>"localhost" } + assert_equal expect_prod, actual["production"] + end + def test_string_connection config = { "production" => "postgres://localhost/foo" } actual = klass.new(config).resolve @@ -69,21 +77,6 @@ module ActiveRecord assert_equal nil, actual[:test] end - def test_sting_with_database_url - ENV['DATABASE_URL'] = "NOT-POSTGRES://localhost/NOT_FOO" - - config = { "production" => "postgres://localhost/foo" } - actual = klass.new(config).resolve - - expected = { "production" => - { "adapter" => "postgresql", - "database" => "foo", - "host" => "localhost" - } - } - assert_equal expected, actual - end - def test_url_sub_key_with_database_url ENV['DATABASE_URL'] = "NOT-POSTGRES://localhost/NOT_FOO" diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb index debacf815c..2b1749cf70 100644 --- a/activerecord/test/cases/invertible_migration_test.rb +++ b/activerecord/test/cases/invertible_migration_test.rb @@ -271,16 +271,19 @@ module ActiveRecord ActiveRecord::Base.table_name_prefix = ActiveRecord::Base.table_name_suffix = '' end - def test_migrate_revert_add_index_with_name - RevertNamedIndexMigration1.new.migrate(:up) - RevertNamedIndexMigration2.new.migrate(:up) - RevertNamedIndexMigration2.new.migrate(:down) - - connection = ActiveRecord::Base.connection - assert connection.index_exists?(:horses, :content), - "index on content should exist" - assert !connection.index_exists?(:horses, :content, name: "horses_index_named"), - "horses_index_named index should not exist" + # MySQL 5.7 and Oracle do not allow to create duplicate indexes on the same columns + unless current_adapter?(:MysqlAdapter, :Mysql2Adapter, :OracleAdapter) + def test_migrate_revert_add_index_with_name + RevertNamedIndexMigration1.new.migrate(:up) + RevertNamedIndexMigration2.new.migrate(:up) + RevertNamedIndexMigration2.new.migrate(:down) + + connection = ActiveRecord::Base.connection + assert connection.index_exists?(:horses, :content), + "index on content should exist" + assert !connection.index_exists?(:horses, :content, name: "horses_index_named"), + "horses_index_named index should not exist" + end end end diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb index 170e9a49eb..9a4d8c6740 100644 --- a/activerecord/test/cases/scoping/default_scoping_test.rb +++ b/activerecord/test/cases/scoping/default_scoping_test.rb @@ -395,4 +395,22 @@ class DefaultScopingTest < ActiveRecord::TestCase threads.each(&:join) end end + + test "additional conditions are ANDed with the default scope" do + scope = DeveloperCalledJamis.where(name: "David") + assert_equal 2, scope.where_values.length + assert_equal [], scope.to_a + end + + test "additional conditions in a scope are ANDed with the default scope" do + scope = DeveloperCalledJamis.david + assert_equal 2, scope.where_values.length + assert_equal [], scope.to_a + end + + test "a scope can remove the condition from the default scope" do + scope = DeveloperCalledJamis.david2 + assert_equal 1, scope.where_values.length + assert_equal Developer.where(name: "David").map(&:id), scope.map(&:id) + end end diff --git a/activerecord/test/fixtures/computers.yml b/activerecord/test/fixtures/computers.yml index daf969d7da..7281a4d768 100644 --- a/activerecord/test/fixtures/computers.yml +++ b/activerecord/test/fixtures/computers.yml @@ -1,4 +1,5 @@ workstation: id: 1 + system: 'Linux' developer: 1 extendedWarranty: 1 diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index 2e2d8a0d37..762259ffa3 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -165,6 +165,8 @@ class DeveloperCalledJamis < ActiveRecord::Base default_scope { where(:name => 'Jamis') } scope :poor, -> { where('salary < 150000') } + scope :david, -> { where name: "David" } + scope :david2, -> { unscoped.where name: "David" } end class PoorDeveloperCalledJamis < ActiveRecord::Base diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb index 8510c596a7..7bb0caf44b 100644 --- a/activerecord/test/models/pirate.rb +++ b/activerecord/test/models/pirate.rb @@ -85,11 +85,3 @@ end class DestructivePirate < Pirate has_one :dependent_ship, :class_name => 'Ship', :foreign_key => :pirate_id, :dependent => :destroy end - -class FamousPirate < ActiveRecord::Base - self.table_name = 'pirates' - - has_many :famous_ships - - validates_presence_of :catchphrase, on: :conference -end diff --git a/activerecord/test/models/ship.rb b/activerecord/test/models/ship.rb index 7a369b9d9a..3da031946f 100644 --- a/activerecord/test/models/ship.rb +++ b/activerecord/test/models/ship.rb @@ -17,11 +17,3 @@ class Ship < ActiveRecord::Base false end end - -class FamousShip < ActiveRecord::Base - self.table_name = 'ships' - - belongs_to :famous_pirate - - validates_presence_of :name, on: :conference -end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 99a53434f6..5f2d6acbcb 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -198,6 +198,7 @@ ActiveRecord::Schema.define do end create_table :computers, force: true do |t| + t.string :system t.integer :developer, null: false t.integer :extendedWarranty, null: false end |