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

Merge pull request #22567 from gsamokovarov/introduce-application-record

Introduce ApplicationRecord, an Active Record layer supertype
This commit is contained in:
Rafael França 2015-12-16 14:37:57 -02:00
commit 1d7d8062b2
16 changed files with 200 additions and 134 deletions

View file

@ -1,3 +1,18 @@
* Introduce ApplicationRecord, an Active Record layer super type.
An `ApplicationRecord` let's engines have models, isolated from the main
application. Plugin authors can use it to distribute extensions as modules
to be included into `ApplicationRecord`, instead of monkey patches. It can
also serve as a place for applications to customize the default
`ActiveRecord::Base` model behaviour.
Newly generated applications have `app/models/application_record.rb`
present by default. Generators are smart enough to recognize that
newly generated models have to inherit from `ApplicationRecord` only if
it's present.
*Genadi Samokovarov*
* Version the API presented to migration classes, so we can change parameter
defaults without breaking existing migrations, or forcing them to be
rewritten through a deprecation cycle.

View file

@ -43,9 +43,16 @@ module ActiveRecord
# Used by the migration template to determine the parent name of the model
def parent_class_name
options[:parent] || "ActiveRecord::Base"
options[:parent] || determine_default_parent_class
end
def determine_default_parent_class
if File.exist?('app/models/application_record.rb')
"ApplicationRecord"
else
"ActiveRecord::Base"
end
end
end
end
end

View file

@ -132,10 +132,10 @@ Creating Active Record Models
-----------------------------
It is very easy to create Active Record models. All you have to do is to
subclass the `ActiveRecord::Base` class and you're good to go:
subclass the `ApplicationRecord` class and you're good to go:
```ruby
class Product < ActiveRecord::Base
class Product < ApplicationRecord
end
```
@ -168,11 +168,12 @@ What if you need to follow a different naming convention or need to use your
Rails application with a legacy database? No problem, you can easily override
the default conventions.
You can use the `ActiveRecord::Base.table_name=` method to specify the table
name that should be used:
`ApplicationRecord` inherits from `ActionController::Base`, which defines a
number of helpful methods. You can use the `ActiveRecord::Base.table_name=`
method to specify the table name that should be used:
```ruby
class Product < ActiveRecord::Base
class Product < ApplicationRecord
self.table_name = "my_products"
end
```
@ -193,7 +194,7 @@ It's also possible to override the column that should be used as the table's
primary key using the `ActiveRecord::Base.primary_key=` method:
```ruby
class Product < ActiveRecord::Base
class Product < ApplicationRecord
self.primary_key = "product_id"
end
```
@ -320,7 +321,7 @@ they raise the exception `ActiveRecord::RecordInvalid` if validation fails.
A quick example to illustrate:
```ruby
class User < ActiveRecord::Base
class User < ApplicationRecord
validates :name, presence: true
end

View file

@ -31,7 +31,7 @@ Callbacks are methods that get called at certain moments of an object's life cyc
In order to use the available callbacks, you need to register them. You can implement the callbacks as ordinary methods and use a macro-style class method to register them as callbacks:
```ruby
class User < ActiveRecord::Base
class User < ApplicationRecord
validates :login, :email, presence: true
before_validation :ensure_login_has_a_value
@ -48,7 +48,7 @@ end
The macro-style class methods can also receive a block. Consider using this style if the code inside your block is so short that it fits in a single line:
```ruby
class User < ActiveRecord::Base
class User < ApplicationRecord
validates :login, :email, presence: true
before_create do
@ -60,7 +60,7 @@ end
Callbacks can also be registered to only fire on certain life cycle events:
```ruby
class User < ActiveRecord::Base
class User < ApplicationRecord
before_validation :normalize_name, on: :create
# :on takes an array as well
@ -126,7 +126,7 @@ The `after_find` callback will be called whenever Active Record loads a record f
The `after_initialize` and `after_find` callbacks have no `before_*` counterparts, but they can be registered just like the other Active Record callbacks.
```ruby
class User < ActiveRecord::Base
class User < ApplicationRecord
after_initialize do |user|
puts "You have initialized an object!"
end
@ -151,7 +151,7 @@ You have initialized an object!
The `after_touch` callback will be called whenever an Active Record object is touched.
```ruby
class User < ActiveRecord::Base
class User < ApplicationRecord
after_touch do |user|
puts "You have touched an object"
end
@ -168,14 +168,14 @@ You have touched an object
It can be used along with `belongs_to`:
```ruby
class Employee < ActiveRecord::Base
class Employee < ApplicationRecord
belongs_to :company, touch: true
after_touch do
puts 'An Employee was touched'
end
end
class Company < ActiveRecord::Base
class Company < ApplicationRecord
has_many :employees
after_touch :log_when_employees_or_company_touched
@ -266,11 +266,11 @@ Relational Callbacks
Callbacks work through model relationships, and can even be defined by them. Suppose an example where a user has many articles. A user's articles should be destroyed if the user is destroyed. Let's add an `after_destroy` callback to the `User` model by way of its relationship to the `Article` model:
```ruby
class User < ActiveRecord::Base
class User < ApplicationRecord
has_many :articles, dependent: :destroy
end
class Article < ActiveRecord::Base
class Article < ApplicationRecord
after_destroy :log_destroy_action
def log_destroy_action
@ -297,7 +297,7 @@ As with validations, we can also make the calling of a callback method condition
You can associate the `:if` and `:unless` options with a symbol corresponding to the name of a predicate method that will get called right before the callback. When using the `:if` option, the callback won't be executed if the predicate method returns false; when using the `:unless` option, the callback won't be executed if the predicate method returns true. This is the most common option. Using this form of registration it is also possible to register several different predicates that should be called to check if the callback should be executed.
```ruby
class Order < ActiveRecord::Base
class Order < ApplicationRecord
before_save :normalize_card_number, if: :paid_with_card?
end
```
@ -307,7 +307,7 @@ end
You can also use a string that will be evaluated using `eval` and hence needs to contain valid Ruby code. You should use this option only when the string represents a really short condition:
```ruby
class Order < ActiveRecord::Base
class Order < ApplicationRecord
before_save :normalize_card_number, if: "paid_with_card?"
end
```
@ -317,7 +317,7 @@ end
Finally, it is possible to associate `:if` and `:unless` with a `Proc` object. This option is best suited when writing short validation methods, usually one-liners:
```ruby
class Order < ActiveRecord::Base
class Order < ApplicationRecord
before_save :normalize_card_number,
if: Proc.new { |order| order.paid_with_card? }
end
@ -328,7 +328,7 @@ end
When writing conditional callbacks, it is possible to mix both `:if` and `:unless` in the same callback declaration:
```ruby
class Comment < ActiveRecord::Base
class Comment < ApplicationRecord
after_create :send_email_to_author, if: :author_wants_emails?,
unless: Proc.new { |comment| comment.article.ignore_comments? }
end
@ -354,7 +354,7 @@ end
When declared inside a class, as above, the callback methods will receive the model object as a parameter. We can now use the callback class in the model:
```ruby
class PictureFile < ActiveRecord::Base
class PictureFile < ApplicationRecord
after_destroy PictureFileCallbacks.new
end
```
@ -374,7 +374,7 @@ end
If the callback method is declared this way, it won't be necessary to instantiate a `PictureFileCallbacks` object.
```ruby
class PictureFile < ActiveRecord::Base
class PictureFile < ApplicationRecord
after_destroy PictureFileCallbacks
end
```
@ -398,7 +398,7 @@ end
By using the `after_commit` callback we can account for this case.
```ruby
class PictureFile < ActiveRecord::Base
class PictureFile < ApplicationRecord
after_commit :delete_picture_file_from_disk, on: [:destroy]
def delete_picture_file_from_disk
@ -420,7 +420,7 @@ common, there are aliases for those operations:
* `after_destroy_commit`
```ruby
class PictureFile < ActiveRecord::Base
class PictureFile < ApplicationRecord
after_destroy_commit :delete_picture_file_from_disk
def delete_picture_file_from_disk

View file

@ -39,7 +39,7 @@ create_table :documents do |t|
end
# app/models/document.rb
class Document < ActiveRecord::Base
class Document < ApplicationRecord
end
# Usage
@ -63,7 +63,7 @@ add_index :books, :tags, using: 'gin'
add_index :books, :ratings, using: 'gin'
# app/models/book.rb
class Book < ActiveRecord::Base
class Book < ApplicationRecord
end
# Usage
@ -97,7 +97,7 @@ ActiveRecord::Schema.define do
end
# app/models/profile.rb
class Profile < ActiveRecord::Base
class Profile < ApplicationRecord
end
# Usage
@ -122,7 +122,7 @@ create_table :events do |t|
end
# app/models/event.rb
class Event < ActiveRecord::Base
class Event < ApplicationRecord
end
# Usage
@ -150,7 +150,7 @@ create_table :events do |t|
end
# app/models/event.rb
class Event < ActiveRecord::Base
class Event < ApplicationRecord
end
# Usage
@ -200,7 +200,7 @@ create_table :contacts do |t|
end
# app/models/contact.rb
class Contact < ActiveRecord::Base
class Contact < ApplicationRecord
end
# Usage
@ -239,7 +239,7 @@ def down
end
# app/models/article.rb
class Article < ActiveRecord::Base
class Article < ApplicationRecord
end
# Usage
@ -294,7 +294,7 @@ create_table :revisions do |t|
end
# app/models/revision.rb
class Revision < ActiveRecord::Base
class Revision < ApplicationRecord
end
# Usage
@ -317,12 +317,12 @@ create_table :comments, id: :uuid, default: 'gen_random_uuid()' do |t|
end
# app/models/post.rb
class Post < ActiveRecord::Base
class Post < ApplicationRecord
has_many :comments
end
# app/models/comment.rb
class Comment < ActiveRecord::Base
class Comment < ApplicationRecord
belongs_to :post
end
```
@ -341,7 +341,7 @@ create_table :users, force: true do |t|
end
# app/models/device.rb
class User < ActiveRecord::Base
class User < ApplicationRecord
end
# Usage
@ -370,7 +370,7 @@ create_table(:devices, force: true) do |t|
end
# app/models/device.rb
class Device < ActiveRecord::Base
class Device < ApplicationRecord
end
# Usage
@ -410,7 +410,7 @@ create_table :devices, id: :uuid, default: 'gen_random_uuid()' do |t|
end
# app/models/device.rb
class Device < ActiveRecord::Base
class Device < ApplicationRecord
end
# Usage
@ -434,7 +434,7 @@ end
execute "CREATE INDEX documents_idx ON documents USING gin(to_tsvector('english', title || ' ' || body));"
# app/models/document.rb
class Document < ActiveRecord::Base
class Document < ApplicationRecord
end
# Usage
@ -484,7 +484,7 @@ CREATE VIEW articles AS
SQL
# app/models/article.rb
class Article < ActiveRecord::Base
class Article < ApplicationRecord
self.primary_key = "id"
def archive!
update_attribute :archived, true

View file

@ -25,7 +25,7 @@ Code examples throughout this guide will refer to one or more of the following m
TIP: All of the following models use `id` as the primary key, unless specified otherwise.
```ruby
class Client < ActiveRecord::Base
class Client < ApplicationRecord
has_one :address
has_many :orders
has_and_belongs_to_many :roles
@ -33,19 +33,19 @@ end
```
```ruby
class Address < ActiveRecord::Base
class Address < ApplicationRecord
belongs_to :client
end
```
```ruby
class Order < ActiveRecord::Base
class Order < ApplicationRecord
belongs_to :client, counter_cache: true
end
```
```ruby
class Role < ActiveRecord::Base
class Role < ApplicationRecord
has_and_belongs_to_many :clients
end
```
@ -740,7 +740,7 @@ SELECT "articles".* FROM "articles" WHERE (id > 10) ORDER BY id desc LIMIT 20
The `reorder` method overrides the default scope order. For example:
```ruby
class Article < ActiveRecord::Base
class Article < ApplicationRecord
has_many :comments, -> { order('posted_at DESC') }
end
@ -889,7 +889,7 @@ This behavior can be turned off by setting `ActiveRecord::Base.lock_optimistical
To override the name of the `lock_version` column, `ActiveRecord::Base` provides a class attribute called `locking_column`:
```ruby
class Client < ActiveRecord::Base
class Client < ApplicationRecord
self.locking_column = :lock_client_column
end
```
@ -970,26 +970,26 @@ Active Record lets you use the names of the [associations](association_basics.ht
For example, consider the following `Category`, `Article`, `Comment`, `Guest` and `Tag` models:
```ruby
class Category < ActiveRecord::Base
class Category < ApplicationRecord
has_many :articles
end
class Article < ActiveRecord::Base
class Article < ApplicationRecord
belongs_to :category
has_many :comments
has_many :tags
end
class Comment < ActiveRecord::Base
class Comment < ApplicationRecord
belongs_to :article
has_one :guest
end
class Guest < ActiveRecord::Base
class Guest < ApplicationRecord
belongs_to :comment
end
class Tag < ActiveRecord::Base
class Tag < ApplicationRecord
belongs_to :article
end
```
@ -1199,7 +1199,7 @@ Scoping allows you to specify commonly-used queries which can be referenced as m
To define a simple scope, we use the `scope` method inside the class, passing the query that we'd like to run when this scope is called:
```ruby
class Article < ActiveRecord::Base
class Article < ApplicationRecord
scope :published, -> { where(published: true) }
end
```
@ -1207,7 +1207,7 @@ end
This is exactly the same as defining a class method, and which you use is a matter of personal preference:
```ruby
class Article < ActiveRecord::Base
class Article < ApplicationRecord
def self.published
where(published: true)
end
@ -1217,7 +1217,7 @@ end
Scopes are also chainable within scopes:
```ruby
class Article < ActiveRecord::Base
class Article < ApplicationRecord
scope :published, -> { where(published: true) }
scope :published_and_commented, -> { published.where("comments_count > 0") }
end
@ -1241,7 +1241,7 @@ category.articles.published # => [published articles belonging to this category]
Your scope can take arguments:
```ruby
class Article < ActiveRecord::Base
class Article < ApplicationRecord
scope :created_before, ->(time) { where("created_at < ?", time) }
end
```
@ -1255,7 +1255,7 @@ Article.created_before(Time.zone.now)
However, this is just duplicating the functionality that would be provided to you by a class method.
```ruby
class Article < ActiveRecord::Base
class Article < ApplicationRecord
def self.created_before(time)
where("created_at < ?", time)
end
@ -1274,7 +1274,7 @@ If we wish for a scope to be applied across all queries to the model we can use
`default_scope` method within the model itself.
```ruby
class Client < ActiveRecord::Base
class Client < ApplicationRecord
default_scope { where("removed_at IS NULL") }
end
```
@ -1290,7 +1290,7 @@ If you need to do more complex things with a default scope, you can alternativel
define it as a class method:
```ruby
class Client < ActiveRecord::Base
class Client < ApplicationRecord
def self.default_scope
# Should return an ActiveRecord::Relation.
end
@ -1301,7 +1301,7 @@ NOTE: The `default_scope` is also applied while creating/building a record.
It is not applied while updating a record. E.g.:
```ruby
class Client < ActiveRecord::Base
class Client < ApplicationRecord
default_scope { where(active: true) }
end
@ -1314,7 +1314,7 @@ Client.unscoped.new # => #<Client id: nil, active: nil>
Just like `where` clauses scopes are merged using `AND` conditions.
```ruby
class User < ActiveRecord::Base
class User < ApplicationRecord
scope :active, -> { where state: 'active' }
scope :inactive, -> { where state: 'inactive' }
end
@ -1343,7 +1343,7 @@ One important caveat is that `default_scope` will be prepended in
`scope` and `where` conditions.
```ruby
class User < ActiveRecord::Base
class User < ApplicationRecord
default_scope { where state: 'pending' }
scope :active, -> { where state: 'active' }
scope :inactive, -> { where state: 'inactive' }
@ -1405,7 +1405,7 @@ Enums
The `enum` macro maps an integer column to a set of possible values.
```ruby
class Book < ActiveRecord::Base
class Book < ApplicationRecord
enum availability: [:available, :unavailable]
end
```
@ -1657,7 +1657,7 @@ a large or often-running query. However, any model method overrides will
not be available. For example:
```ruby
class Client < ActiveRecord::Base
class Client < ApplicationRecord
def name
"I am #{super}"
end
@ -1692,7 +1692,7 @@ Person.ids
```
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
self.primary_key = "person_id"
end

View file

@ -20,7 +20,7 @@ Validations Overview
Here's an example of a very simple validation:
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :name, presence: true
end
@ -80,7 +80,7 @@ method to determine whether an object is already in the database or not.
Consider the following simple Active Record class:
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
end
```
@ -157,7 +157,7 @@ and returns true if no errors were found in the object, and false otherwise.
As you saw above:
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :name, presence: true
end
@ -175,7 +175,7 @@ even if it's technically invalid, because validations are automatically run
only when the object is saved, such as with the `create` or `save` methods.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :name, presence: true
end
@ -221,7 +221,7 @@ it doesn't verify the validity of the object as a whole. It only checks to see
whether there are errors found on an individual attribute of the object.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :name, presence: true
end
@ -239,7 +239,7 @@ To check which validations failed on an invalid attribute, you can use
key to get the symbol of the validator:
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :name, presence: true
end
@ -285,7 +285,7 @@ the field does exist in your database, the `accept` option must be set to
`true` or else the validation will not run.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :terms_of_service, acceptance: true
end
```
@ -297,7 +297,7 @@ It can receive an `:accept` option, which determines the value that will be
considered acceptance. It defaults to "1" and can be easily changed.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :terms_of_service, acceptance: { accept: 'yes' }
end
```
@ -309,7 +309,7 @@ and they also need to be validated. When you try to save your object, `valid?`
will be called upon each one of the associated objects.
```ruby
class Library < ActiveRecord::Base
class Library < ApplicationRecord
has_many :books
validates_associated :books
end
@ -332,7 +332,7 @@ or a password. This validation creates a virtual attribute whose name is the
name of the field that has to be confirmed with "_confirmation" appended.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :email, confirmation: true
end
```
@ -349,7 +349,7 @@ confirmation, make sure to add a presence check for the confirmation attribute
(we'll take a look at `presence` later on in this guide):
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :email, confirmation: true
validates :email_confirmation, presence: true
end
@ -360,7 +360,7 @@ confirmation constraint will be case sensitive or not. This option defaults to
true.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :email, confirmation: { case_sensitive: false }
end
```
@ -373,7 +373,7 @@ This helper validates that the attributes' values are not included in a given
set. In fact, this set can be any enumerable object.
```ruby
class Account < ActiveRecord::Base
class Account < ApplicationRecord
validates :subdomain, exclusion: { in: %w(www us ca jp),
message: "%{value} is reserved." }
end
@ -393,7 +393,7 @@ This helper validates the attributes' values by testing whether they match a
given regular expression, which is specified using the `:with` option.
```ruby
class Product < ActiveRecord::Base
class Product < ApplicationRecord
validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/,
message: "only allows letters" }
end
@ -409,7 +409,7 @@ This helper validates that the attributes' values are included in a given set.
In fact, this set can be any enumerable object.
```ruby
class Coffee < ActiveRecord::Base
class Coffee < ApplicationRecord
validates :size, inclusion: { in: %w(small medium large),
message: "%{value} is not a valid size" }
end
@ -428,7 +428,7 @@ This helper validates the length of the attributes' values. It provides a
variety of options, so you can specify length constraints in different ways:
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :name, length: { minimum: 2 }
validates :bio, length: { maximum: 500 }
validates :password, length: { in: 6..20 }
@ -451,7 +451,7 @@ number corresponding to the length constraint being used. You can still use the
`:message` option to specify an error message.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :bio, length: { maximum: 1000,
too_long: "%{count} characters is the maximum allowed" }
end
@ -483,7 +483,7 @@ WARNING. Note that the regular expression above allows a trailing newline
character.
```ruby
class Player < ActiveRecord::Base
class Player < ApplicationRecord
validates :points, numericality: true
validates :games_played, numericality: { only_integer: true }
end
@ -521,7 +521,7 @@ This helper validates that the specified attributes are not empty. It uses the
is, a string that is either empty or consists of whitespace.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :name, :login, :email, presence: true
end
```
@ -531,7 +531,7 @@ whether the associated object itself is present, and not the foreign key used
to map the association.
```ruby
class LineItem < ActiveRecord::Base
class LineItem < ApplicationRecord
belongs_to :order
validates :order, presence: true
end
@ -541,7 +541,7 @@ In order to validate associated records whose presence is required, you must
specify the `:inverse_of` option for the association:
```ruby
class Order < ActiveRecord::Base
class Order < ApplicationRecord
has_many :line_items, inverse_of: :order
end
```
@ -568,7 +568,7 @@ This helper validates that the specified attributes are absent. It uses the
is, a string that is either empty or consists of whitespace.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :name, :login, :email, absence: true
end
```
@ -578,7 +578,7 @@ whether the associated object itself is absent, and not the foreign key used
to map the association.
```ruby
class LineItem < ActiveRecord::Base
class LineItem < ApplicationRecord
belongs_to :order
validates :order, absence: true
end
@ -588,7 +588,7 @@ In order to validate associated records whose absence is required, you must
specify the `:inverse_of` option for the association:
```ruby
class Order < ActiveRecord::Base
class Order < ApplicationRecord
has_many :line_items, inverse_of: :order
end
```
@ -611,7 +611,7 @@ with the same value for a column that you intend to be unique. To avoid that,
you must create a unique index on that column in your database.
```ruby
class Account < ActiveRecord::Base
class Account < ApplicationRecord
validates :email, uniqueness: true
end
```
@ -623,7 +623,7 @@ There is a `:scope` option that you can use to specify one or more attributes th
are used to limit the uniqueness check:
```ruby
class Holiday < ActiveRecord::Base
class Holiday < ApplicationRecord
validates :name, uniqueness: { scope: :year,
message: "should happen once per year" }
end
@ -635,7 +635,7 @@ uniqueness constraint will be case sensitive or not. This option defaults to
true.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :name, uniqueness: { case_sensitive: false }
end
```
@ -658,7 +658,7 @@ class GoodnessValidator < ActiveModel::Validator
end
end
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates_with GoodnessValidator
end
```
@ -686,7 +686,7 @@ class GoodnessValidator < ActiveModel::Validator
end
end
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates_with GoodnessValidator, fields: [:first_name, :last_name]
end
```
@ -699,7 +699,7 @@ If your validator is complex enough that you want instance variables, you can
easily use a plain old Ruby object instead:
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validate do |person|
GoodnessValidator.new(person).validate
end
@ -728,7 +728,7 @@ passed to `validates_each` will be tested against it. In the following example,
we don't want names and surnames to begin with lower case.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates_each :name, :surname do |record, attr, value|
record.errors.add(attr, 'must start with upper case') if value =~ /\A[[:lower:]]/
end
@ -751,7 +751,7 @@ The `:allow_nil` option skips the validation when the value being validated is
`nil`.
```ruby
class Coffee < ActiveRecord::Base
class Coffee < ApplicationRecord
validates :size, inclusion: { in: %w(small medium large),
message: "%{value} is not a valid size" }, allow_nil: true
end
@ -764,7 +764,7 @@ will let validation pass if the attribute's value is `blank?`, like `nil` or an
empty string for example.
```ruby
class Topic < ActiveRecord::Base
class Topic < ApplicationRecord
validates :title, length: { is: 5 }, allow_blank: true
end
@ -787,7 +787,7 @@ A `Proc` `:message` value is given two arguments: a message key for i18n, and
a hash with `:model`, `:attribute`, and `:value` key-value pairs.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
# Hard-coded message
validates :name, presence: { message: "must be given please" }
@ -818,7 +818,7 @@ new record is created or `on: :update` to run the validation only when a record
is updated.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
# it will be possible to update email with a duplicated value
validates :email, uniqueness: true, on: :create
@ -837,7 +837,7 @@ You can also specify validations to be strict and raise
`ActiveModel::StrictValidationFailed` when the object is invalid.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :name, presence: { strict: true }
end
@ -847,7 +847,7 @@ Person.new.valid? # => ActiveModel::StrictValidationFailed: Name can't be blank
There is also the ability to pass a custom exception to the `:strict` option.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :token, presence: true, uniqueness: true, strict: TokenGenerationException
end
@ -871,7 +871,7 @@ to the name of a method that will get called right before validation happens.
This is the most commonly used option.
```ruby
class Order < ActiveRecord::Base
class Order < ApplicationRecord
validates :card_number, presence: true, if: :paid_with_card?
def paid_with_card?
@ -887,7 +887,7 @@ contain valid Ruby code. You should use this option only when the string
represents a really short condition.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :surname, presence: true, if: "name.nil?"
end
```
@ -900,7 +900,7 @@ inline condition instead of a separate method. This option is best suited for
one-liners.
```ruby
class Account < ActiveRecord::Base
class Account < ApplicationRecord
validates :password, confirmation: true,
unless: Proc.new { |a| a.password.blank? }
end
@ -912,7 +912,7 @@ Sometimes it is useful to have multiple validations use one condition. It can
be easily achieved using `with_options`.
```ruby
class User < ActiveRecord::Base
class User < ApplicationRecord
with_options if: :is_admin? do |admin|
admin.validates :password, length: { minimum: 10 }
admin.validates :email, presence: true
@ -930,7 +930,7 @@ should happen, an `Array` can be used. Moreover, you can apply both `:if` and
`:unless` to the same validation.
```ruby
class Computer < ActiveRecord::Base
class Computer < ApplicationRecord
validates :mouse, presence: true,
if: ["market.retail?", :desktop?],
unless: Proc.new { |c| c.trackpad.present? }
@ -984,7 +984,7 @@ class EmailValidator < ActiveModel::EachValidator
end
end
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :email, presence: true, email: true
end
```
@ -1008,7 +1008,7 @@ so your custom validation methods should add errors to it when you
wish validation to fail:
```ruby
class Invoice < ActiveRecord::Base
class Invoice < ApplicationRecord
validate :expiration_date_cannot_be_in_the_past,
:discount_cannot_be_greater_than_total_value
@ -1032,7 +1032,7 @@ custom validations by giving an `:on` option to the `validate` method,
with either: `:create` or `:update`.
```ruby
class Invoice < ActiveRecord::Base
class Invoice < ApplicationRecord
validate :active_customer, on: :create
def active_customer
@ -1053,7 +1053,7 @@ The following is a list of the most commonly used methods. Please refer to the `
Returns an instance of the class `ActiveModel::Errors` containing all errors. Each key is the attribute name and the value is an array of strings with all errors.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :name, presence: true, length: { minimum: 3 }
end
@ -1072,7 +1072,7 @@ person.errors.messages # => {}
`errors[]` is used when you want to check the error messages for a specific attribute. It returns an array of strings with all error messages for the given attribute, each string with one error message. If there are no errors related to the attribute, it returns an empty array.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :name, presence: true, length: { minimum: 3 }
end
@ -1097,7 +1097,7 @@ The `add` method lets you add an error message related to a particular attribute
The `errors.full_messages` method (or its equivalent, `errors.to_a`) returns the error messages in a user-friendly format, with the capitalized attribute name prepended to each message, as shown in the examples below.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
def a_method_used_for_validation_purposes
errors.add(:name, "cannot contain the characters !@#%*()_-+=")
end
@ -1115,7 +1115,7 @@ person.errors.full_messages
An equivalent to `errors#add` is to use `<<` to append a message to the `errors.messages` array for an attribute:
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
def a_method_used_for_validation_purposes
errors.messages[:name] << "cannot contain the characters !@#%*()_-+="
end
@ -1136,7 +1136,7 @@ You can specify a validator type to the returned error details hash using the
`errors.add` method.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
def a_method_used_for_validation_purposes
errors.add(:name, :invalid_characters)
end
@ -1152,7 +1152,7 @@ To improve the error details to contain the unallowed characters set for instanc
you can pass additional keys to `errors.add`.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
def a_method_used_for_validation_purposes
errors.add(:name, :invalid_characters, not_allowed: "!@#%*()_-+=")
end
@ -1172,7 +1172,7 @@ validator type.
You can add error messages that are related to the object's state as a whole, instead of being related to a specific attribute. You can use this method when you want to say that the object is invalid, no matter the values of its attributes. Since `errors[:base]` is an array, you can simply add a string to it and it will be used as an error message.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
def a_method_used_for_validation_purposes
errors[:base] << "This person is invalid because ..."
end
@ -1184,7 +1184,7 @@ end
The `clear` method is used when you intentionally want to clear all the messages in the `errors` collection. Of course, calling `errors.clear` upon an invalid object won't actually make it valid: the `errors` collection will now be empty, but the next time you call `valid?` or any method that tries to save this object to the database, the validations will run again. If any of the validations fail, the `errors` collection will be filled again.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :name, presence: true, length: { minimum: 3 }
end
@ -1207,7 +1207,7 @@ p.errors[:name]
The `size` method returns the total number of error messages for the object.
```ruby
class Person < ActiveRecord::Base
class Person < ApplicationRecord
validates :name, presence: true, length: { minimum: 3 }
end

View file

@ -508,7 +508,7 @@ Turning the model into this:
```ruby
module Blorgh
class Article < ActiveRecord::Base
class Article < ApplicationRecord
has_many :comments
end
end
@ -1129,7 +1129,7 @@ end
```ruby
# Blorgh/app/models/article.rb
class Article < ActiveRecord::Base
class Article < ApplicationRecord
has_many :comments
end
```
@ -1150,7 +1150,7 @@ end
```ruby
# Blorgh/app/models/article.rb
class Article < ActiveRecord::Base
class Article < ApplicationRecord
has_many :comments
def summary
"#{title}"
@ -1171,7 +1171,7 @@ classes at run time allowing you to significantly modularize your code.
```ruby
# MyApp/app/models/blorgh/article.rb
class Blorgh::Article < ActiveRecord::Base
class Blorgh::Article < ApplicationRecord
include Blorgh::Concerns::Models::Article
def time_since_created
@ -1187,7 +1187,7 @@ end
```ruby
# Blorgh/app/models/article.rb
class Article < ActiveRecord::Base
class Article < ApplicationRecord
include Blorgh::Concerns::Models::Article
end
```

View file

@ -17,7 +17,7 @@ After reading this guide, you will know:
This guide describes how to build a test-driven plugin that will:
* Extend core Ruby classes like Hash and String.
* Add methods to `ActiveRecord::Base` in the tradition of the `acts_as` plugins.
* Add methods to `ApplicationRecord` in the tradition of the `acts_as` plugins.
* Give you information about where to put generators in your plugin.
For the purpose of this guide pretend for a moment that you are an avid bird watcher.
@ -182,7 +182,6 @@ To start out, write a failing test that shows the behavior you'd like:
require 'test_helper'
class ActsAsYaffleTest < ActiveSupport::TestCase
def test_a_hickwalls_yaffle_text_field_should_be_last_squawk
assert_equal "last_squawk", Hickwall.yaffle_text_field
end
@ -190,7 +189,6 @@ class ActsAsYaffleTest < ActiveSupport::TestCase
def test_a_wickwalls_yaffle_text_field_should_be_last_tweet
assert_equal "last_tweet", Wickwall.yaffle_text_field
end
end
```
@ -234,22 +232,22 @@ like yaffles.
```ruby
# test/dummy/app/models/hickwall.rb
class Hickwall < ActiveRecord::Base
class Hickwall < ApplicationRecord
acts_as_yaffle
end
# test/dummy/app/models/wickwall.rb
class Wickwall < ActiveRecord::Base
class Wickwall < ApplicationRecord
acts_as_yaffle yaffle_text_field: :last_tweet
end
```
We will also add code to define the `acts_as_yaffle` method.
```ruby
# yaffle/lib/yaffle/acts_as_yaffle.rb
module Yaffle
module ActsAsYaffle
extend ActiveSupport::Concern
@ -265,7 +263,13 @@ module Yaffle
end
end
ActiveRecord::Base.include(Yaffle::ActsAsYaffle)
# test/dummy/app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
include Yaffle::ActsAsYaffle
self.abstract_class = true
end
```
You can then return to the root directory (`cd ../..`) of your plugin and rerun the tests using `rake`.
@ -308,7 +312,13 @@ module Yaffle
end
end
ActiveRecord::Base.include(Yaffle::ActsAsYaffle)
# test/dummy/app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
include Yaffle::ActsAsYaffle
self.abstract_class = true
end
```
When you run `rake`, you should see the tests all pass:
@ -329,7 +339,6 @@ To start out, write a failing test that shows the behavior you'd like:
require 'test_helper'
class ActsAsYaffleTest < ActiveSupport::TestCase
def test_a_hickwalls_yaffle_text_field_should_be_last_squawk
assert_equal "last_squawk", Hickwall.yaffle_text_field
end
@ -382,7 +391,13 @@ module Yaffle
end
end
ActiveRecord::Base.include(Yaffle::ActsAsYaffle)
# test/dummy/app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
include Yaffle::ActsAsYaffle
self.abstract_class = true
end
```
Run `rake` one final time and you should see:

View file

@ -293,6 +293,12 @@ module Rails
end
end
def delete_application_record_skipping_active_record
if options[:skip_active_record]
remove_file 'app/models/application_record.rb'
end
end
def delete_active_record_initializers_skipping_active_record
if options[:skip_active_record]
remove_file 'config/initializers/active_record_belongs_to_required_by_default.rb'

View file

@ -0,0 +1,3 @@
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end

View file

@ -0,0 +1,6 @@
<%= wrap_in_modules <<-rb.strip_heredoc
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end
rb
%>

View file

@ -228,6 +228,7 @@ module ApplicationTests
end
test "the application can be eager loaded even when there are no frameworks" do
FileUtils.rm_rf("#{app_path}/app/models/application_record.rb")
FileUtils.rm_rf("#{app_path}/config/environments")
add_to_config <<-RUBY
config.eager_load = true

View file

@ -98,7 +98,7 @@ module ApplicationTests
end
def test_code_statistics_sanity
assert_match "Code LOC: 7 Test LOC: 0 Code to Test Ratio: 1:0.0",
assert_match "Code LOC: 10 Test LOC: 0 Code to Test Ratio: 1:0.0",
Dir.chdir(app_path){ `bin/rake stats` }
end

View file

@ -334,6 +334,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
run_generator [destination_root, "--skip-active-record"]
assert_no_file "config/database.yml"
assert_no_file "config/initializers/active_record_belongs_to_required_by_default.rb"
assert_no_file "app/models/application_record.rb"
assert_file "config/application.rb", /#\s+require\s+["']active_record\/railtie["']/
assert_file "test/test_helper.rb" do |helper_content|
assert_no_match(/fixtures :all/, helper_content)

View file

@ -35,6 +35,17 @@ class ModelGeneratorTest < Rails::Generators::TestCase
assert_no_migration "db/migrate/create_accounts.rb"
end
def test_model_with_existent_application_record
mkdir_p "#{destination_root}/app/models"
touch "#{destination_root}/app/models/application_record.rb"
Dir.chdir(destination_root) do
run_generator ["account"]
end
assert_file "app/models/account.rb", /class Account < ApplicationRecord/
end
def test_plural_names_are_singularized
content = run_generator ["accounts".freeze]
assert_file "app/models/account.rb", /class Account < ActiveRecord::Base/