diff options
author | Xavier Noria <fxn@hashref.com> | 2010-07-26 00:23:57 +0200 |
---|---|---|
committer | Xavier Noria <fxn@hashref.com> | 2010-07-26 00:23:57 +0200 |
commit | 6aa12535881e00f156cf7ddacaba123ed181e015 (patch) | |
tree | 9a845318d1b41d1482304eafa235edd60ac7bd41 /activerecord | |
parent | df95948d455b3a5f75fc01d41375dcd840549633 (diff) | |
parent | 7fb7a2bd69b86ee0a0e832f597f11d8cc1c787c9 (diff) | |
download | rails-6aa12535881e00f156cf7ddacaba123ed181e015.tar.gz rails-6aa12535881e00f156cf7ddacaba123ed181e015.tar.bz2 rails-6aa12535881e00f156cf7ddacaba123ed181e015.zip |
Merge remote branch 'docrails/master'
Diffstat (limited to 'activerecord')
-rw-r--r-- | activerecord/README.rdoc | 242 | ||||
-rw-r--r-- | activerecord/lib/active_record/base.rb | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/reflection.rb | 31 |
3 files changed, 82 insertions, 193 deletions
diff --git a/activerecord/README.rdoc b/activerecord/README.rdoc index 0446180207..8dbd6c82b5 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 @@ -307,15 +195,15 @@ Admit the Database: * Doesn't attempt to duplicate or replace data definitions -== Download +== Download and installation The latest version of Active Record can be installed with Rubygems: -* gem install activerecord + % [sudo] gem install activerecord -Documentation can be found at +Source code can be downloaded as part of the Rails project on GitHub -* http://api.rubyonrails.org +* http://github.com/rails/rails/tree/master/activerecord/ == License @@ -325,12 +213,10 @@ Active Record is released under the MIT license. == Support -The Active Record homepage is http://www.rubyonrails.com. You can find the Active Record -RubyForge page at http://rubyforge.org/projects/activerecord. And as Jim from Rake says: +API documentation is at + +* http://api.rubyonrails.com - Feel free to submit commits or feature requests. If you send a patch, - remember to update the corresponding unit tests. If fact, I prefer - new feature to be submitted in the form of new unit tests. +Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here: -For other information, feel free to ask on the rubyonrails-talk -(http://groups.google.com/group/rubyonrails-talk) mailing list. +* https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 19ccf75b00..c589b32dd8 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1030,7 +1030,7 @@ module ActiveRecord #:nodoc: # class Article < ActiveRecord::Base # def self.find_with_exclusive_scope # with_scope(:find => where(:blog_id => 1).limit(1)) do - # with_exclusive_scope(:find => limit(10)) + # with_exclusive_scope(:find => limit(10)) do # all # => SELECT * from articles LIMIT 10 # end # end diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index a82e5d7ed1..09d7f8699e 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -50,7 +50,7 @@ module ActiveRecord # Returns an array of AssociationReflection objects for all the # associations in the class. If you only want to reflect on a certain # association type, pass in the symbol (<tt>:has_many</tt>, <tt>:has_one</tt>, - # <tt>:belongs_to</tt>) for that as the first parameter. + # <tt>:belongs_to</tt>) as the first parameter. # # Example: # @@ -62,9 +62,9 @@ module ActiveRecord macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections end - # Returns the AssociationReflection object for the named +association+ (use the symbol). Example: + # Returns the AssociationReflection object for the +association+ (use the symbol). # - # Account.reflect_on_association(:owner) # returns the owner AssociationReflection + # Account.reflect_on_association(:owner) # returns the owner AssociationReflection # Invoice.reflect_on_association(:line_items).macro # returns :has_many # def reflect_on_association(association) @@ -88,34 +88,37 @@ module ActiveRecord @macro, @name, @options, @active_record = macro, name, options, active_record end - # Returns the name of the macro. For example, <tt>composed_of :balance, - # :class_name => 'Money'</tt> will return <tt>:balance</tt> or for - # <tt>has_many :clients</tt> it will return <tt>:clients</tt>. + # Returns the name of the macro. + # <tt>composed_of :balance, :class_name => 'Money'</tt> will return <tt>:balance</tt> + # <tt>has_many :clients</tt> will return <tt>:clients</tt> def name @name end - # Returns the macro type. For example, + # Returns the macro type. # <tt>composed_of :balance, :class_name => 'Money'</tt> will return <tt>:composed_of</tt> - # or for <tt>has_many :clients</tt> will return <tt>:has_many</tt>. + # <tt>has_many :clients</tt> will return <tt>:has_many</tt> def macro @macro end - # Returns the hash of options used for the macro. For example, it would return <tt>{ :class_name => "Money" }</tt> for - # <tt>composed_of :balance, :class_name => 'Money'</tt> or +{}+ for <tt>has_many :clients</tt>. + # Returns the hash of options used for the macro. + # <tt>composed_of :balance, :class_name => 'Money'</tt> will return <tt>{ :class_name => "Money" }</tt> + # <tt>has_many :clients</tt> will return +{}+ def options @options end - # Returns the class for the macro. For example, <tt>composed_of :balance, :class_name => 'Money'</tt> returns the Money - # class and <tt>has_many :clients</tt> returns the Client class. + # Returns the class for the macro. + # <tt>composed_of :balance, :class_name => 'Money'</tt> will return the Money class + # <tt>has_many :clients</tt> will return the Client class def klass @klass ||= class_name.constantize end - # Returns the class name for the macro. For example, <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>'Money'</tt> - # and <tt>has_many :clients</tt> returns <tt>'Client'</tt>. + # Returns the class name for the macro. + # <tt>composed_of :balance, :class_name => 'Money'</tt> will return <tt>'Money'</tt> + # <tt>has_many :clients</tt> will return <tt>'Client'</tt> def class_name @class_name ||= options[:class_name] || derive_class_name end |