diff --git a/activerecord/README.rdoc b/activerecord/README.rdoc index efa3c7e7ac71d62bdd86652011bb4ab90cfca2d2..8dbd6c82b5362c5400f409caded2d1ce797ee9c5 100644 --- a/activerecord/README.rdoc +++ b/activerecord/README.rdoc @@ -1,49 +1,52 @@ -= Active Record -- Object-relation mapping put on rails += Active Record -- Object-relational mapping put on rails -Active Record connects business objects and database tables to create a persistable -domain model where logic and data are presented in one wrapping. It's an implementation -of the object-relational mapping (ORM) pattern[http://www.martinfowler.com/eaaCatalog/activeRecord.html] -by the same name as described by Martin Fowler: +Active Record connects classes to relational database tables to establish an +almost zero-configuration persistence layer for applications. The library +provides a base class that, when subclassed, sets up a mapping between the new +class and an existing table in the database. In context of an application, +these classes are commonly referred to as *models*. Models can also be +connected to other models; this is done by defining *associations*. - "An object that wraps a row in a database table or view, encapsulates - the database access, and adds domain logic on that data." +Active Record relies heavily on naming in that it uses class and association +names to establish mappings between respective database tables and foreign key +columns. Although these mappings can be defined explicitly, it's recommended +to follow naming conventions, especially when getting started with the +library. -Active Record's main contribution to the pattern is to relieve the original of two stunting problems: -lack of associations and inheritance. By adding a simple domain language-like set of macros to describe -the former and integrating the Single Table Inheritance pattern for the latter, Active Record narrows the -gap of functionality between the data mapper and active record approach. - -A short rundown of the major features: +A short rundown of some of the major features: * Automated mapping between classes and tables, attributes and columns. - class Product < ActiveRecord::Base; end + class Product < ActiveRecord::Base + end - ...is automatically mapped to the table named "products", such as: + The Product class is automatically mapped to the table named "products", + which might look like this: CREATE TABLE products ( id int(11) NOT NULL auto_increment, name varchar(255), PRIMARY KEY (id) ); - - ...which again gives Product#name and Product#name=(new_name) + + This would also define the following accessors: `Product#name` and + `Product#name=(new_name)` {Learn more}[link:classes/ActiveRecord/Base.html] -* Associations between objects controlled by simple meta-programming macros. +* Associations between objects defined by simple class methods. class Firm < ActiveRecord::Base has_many :clients has_one :account - belongs_to :conglomorate + belongs_to :conglomerate end {Learn more}[link:classes/ActiveRecord/Associations/ClassMethods.html] -* Aggregations of value objects controlled by simple meta-programming macros. +* Aggregations of value objects. class Account < ActiveRecord::Base composed_of :balance, :class_name => "Money", @@ -65,23 +68,19 @@ A short rundown of the major features: end {Learn more}[link:classes/ActiveRecord/Validations.html] - -* Callbacks as methods or queues on the entire lifecycle (instantiation, saving, destroying, validating, etc). - class Person < ActiveRecord::Base - def before_destroy # is called just before Person#destroy - CreditCard.find(credit_card_id).destroy - end - end - class Account < ActiveRecord::Base - after_find :eager_load, 'self.class.announce(#{id})' +* Callbacks available for the entire lifecycle (instantiation, saving, destroying, validating, etc.) + + class Person < ActiveRecord::Base + before_destroy :invalidate_payment_plan + # the `invalidate_payment_plan` method gets called just before Person#destroy end {Learn more}[link:classes/ActiveRecord/Callbacks.html] -* Observers for the entire lifecycle +* Observers that react to changes in a model class CommentObserver < ActiveRecord::Observer def after_create(comment) # is called just after Comment#save @@ -122,40 +121,24 @@ A short rundown of the major features: {Learn more}[link:classes/ActiveRecord/Reflection/ClassMethods.html] -* Direct manipulation (instead of service invocation) +* Database abstraction through simple adapters - So instead of (Hibernate[http://www.hibernate.org/] example): + # connect to SQLite3 + ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => "dbfile.sqlite3") - long pkId = 1234; - DomesticCat pk = (DomesticCat) sess.load( Cat.class, new Long(pkId) ); - // something interesting involving a cat... - sess.save(cat); - sess.flush(); // force the SQL INSERT + # connect to MySQL with authentication + ActiveRecord::Base.establish_connection( + :adapter => "mysql", + :host => "localhost", + :username => "me", + :password => "secret", + :database => "activerecord" + ) - Active Record lets you: - - pkId = 1234 - cat = Cat.find(pkId) - # something even more interesting involving the same cat... - cat.save - - {Learn more}[link:classes/ActiveRecord/Base.html] - - -* Database abstraction through simple adapters (~100 lines) with a shared connector - - ActiveRecord::Base.establish_connection(:adapter => "sqlite", :database => "dbfile") - - ActiveRecord::Base.establish_connection( - :adapter => "mysql", - :host => "localhost", - :username => "me", - :password => "secret", - :database => "activerecord" - ) - - {Learn more}[link:classes/ActiveRecord/Base.html#M000081] and read about the built-in support for - MySQL[link:classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html], PostgreSQL[link:classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], SQLite[link:classes/ActiveRecord/ConnectionAdapters/SQLiteAdapter.html], Oracle[link:classes/ActiveRecord/ConnectionAdapters/OracleAdapter.html], SQLServer[link:classes/ActiveRecord/ConnectionAdapters/SQLServerAdapter.html], and DB2[link:classes/ActiveRecord/ConnectionAdapters/DB2Adapter.html]. + {Learn more}[link:classes/ActiveRecord/Base.html] and read about the built-in support for + MySQL[link:classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html], + PostgreSQL[link:classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], and + SQLite3[link:classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html]. * Logging support for Log4r[http://log4r.sourceforge.net] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc] @@ -169,11 +152,11 @@ A short rundown of the major features: class AddSystemSettings < ActiveRecord::Migration def self.up create_table :system_settings do |t| - t.string :name - t.string :label - t.text :value - t.string :type - t.integer :position + t.string :name + t.string :label + t.text :value + t.string :type + t.integer :position end SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1 @@ -186,110 +169,15 @@ A short rundown of the major features: {Learn more}[link:classes/ActiveRecord/Migration.html] -== Simple example (1/2): Defining tables and classes (using MySQL) - -Data definitions are specified only in the database. Active Record queries the database for -the column names (that then serves to determine which attributes are valid) on regular -object instantiation through the new constructor and relies on the column names in the rows -with the finders. - - # CREATE TABLE companies ( - # id int(11) unsigned NOT NULL auto_increment, - # client_of int(11), - # name varchar(255), - # type varchar(100), - # PRIMARY KEY (id) - # ) - -Active Record automatically links the "Company" object to the "companies" table - - class Company < ActiveRecord::Base - has_many :people, :class_name => "Person" - end - - class Firm < Company - has_many :clients - - def people_with_all_clients - clients.inject([]) { |people, client| people + client.people } - end - end - -The foreign_key is only necessary because we didn't use "firm_id" in the data definition - - class Client < Company - belongs_to :firm, :foreign_key => "client_of" - end - - # CREATE TABLE people ( - # id int(11) unsigned NOT NULL auto_increment, - # name text, - # company_id text, - # PRIMARY KEY (id) - # ) -Active Record will also automatically link the "Person" object to the "people" table - - class Person < ActiveRecord::Base - belongs_to :company - end - -== Simple example (2/2): Using the domain - -Picking a database connection for all the Active Records - - ActiveRecord::Base.establish_connection( - :adapter => "mysql", - :host => "localhost", - :username => "me", - :password => "secret", - :database => "activerecord" - ) - -Create some fixtures - - firm = Firm.new("name" => "Next Angle") - # SQL: INSERT INTO companies (name, type) VALUES("Next Angle", "Firm") - firm.save - - client = Client.new("name" => "37signals", "client_of" => firm.id) - # SQL: INSERT INTO companies (name, client_of, type) VALUES("37signals", 1, "Firm") - client.save - -Lots of different finders - - # SQL: SELECT * FROM companies WHERE id = 1 - next_angle = Company.find(1) - - # SQL: SELECT * FROM companies WHERE id = 1 AND type = 'Firm' - next_angle = Firm.find(1) - - # SQL: SELECT * FROM companies WHERE id = 1 AND name = 'Next Angle' - next_angle = Company.find(:first, :conditions => "name = 'Next Angle'") - - next_angle = Firm.find_by_sql("SELECT * FROM companies WHERE id = 1").first - -The supertype, Company, will return subtype instances - - Firm === next_angle - -All the dynamic methods added by the has_many macro - - next_angle.clients.empty? # true - next_angle.clients.size # total number of clients - all_clients = next_angle.clients - -Constrained finds makes access security easier when ID comes from a web-app - - # SQL: SELECT * FROM companies WHERE client_of = 1 AND type = 'Client' AND id = 2 - thirty_seven_signals = next_angle.clients.find(2) - -Bi-directional associations thanks to the "belongs_to" macro - - thirty_seven_signals.firm.nil? # true +== Philosophy +Active Record is an implementation of the object-relational mapping (ORM) +pattern[http://www.martinfowler.com/eaaCatalog/activeRecord.html] by the same +name described by Martin Fowler: -== Philosophy + "An object that wraps a row in a database table or view, + encapsulates the database access, and adds domain logic on that data." Active Record attempts to provide a coherent wrapper as a solution for the inconvenience that is object-relational mapping. The prime directive for this mapping has been to minimize