Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #7747: active_record_readme_revised.diff

File active_record_readme_revised.diff, 21.4 kB (added by fearoffish, 1 year ago)
  • README

    old new  
    1 = Active Record -- Object-relation mapping put on rails 
     1= Active Record 
    22 
    3 Active Record connects business objects and database tables to create a persistable 
    4 domain model where logic and data are presented in one wrapping. It's an implementation  
    5 of the object-relational mapping (ORM) pattern[http://www.martinfowler.com/eaaCatalog/activeRecord.html]  
     3Active Record is the glue between your application and the database.  In essence, Active Record  
     4represents relational data from databases in an object-relational manner.  Whenever you want 
     5records from the database, Active Record is the class you'll use.  Active Record connects  
     6business objects and database tables to create a persist-able domain model where logic and  
     7data are presented in one wrapping. It's an implementation of the object-relational mapping (ORM) 
     8pattern[http://www.martinfowler.com/eaaCatalog/activeRecord.html]  
    69by the same name as described by Martin Fowler: 
    710 
    8   "An object that wraps a row in a database table or view, encapsulates  
    9        the database access, and adds domain logic on that data." 
     11    "An object that wraps a row in a database table or view, encapsulates  
     12         the database access, and adds domain logic on that data."   
    1013 
    1114Active Record's main contribution to the pattern is to relieve the original of two stunting problems: 
    1215lack of associations and inheritance. By adding a simple domain language-like set of macros to describe 
    1316the former and integrating the Single Table Inheritance pattern for the latter, Active Record narrows the 
    1417gap of functionality between the data mapper and active record approach. 
    1518 
    16 A short rundown of the major features: 
    1719 
    18 * Automated mapping between classes and tables, attributes and columns. 
     20== Philosophy  
    1921 
    20    class Product < ActiveRecord::Base; end 
    21     
    22    ...is automatically mapped to the table named "products", such as: 
    23     
    24    CREATE TABLE products ( 
    25      id int(11) NOT NULL auto_increment, 
    26      name varchar(255), 
    27      PRIMARY KEY  (id) 
    28    ); 
     22Active Record attempts to provide a coherent wrapper as a solution for the inconvenience that is  
     23object-relational mapping. The prime directive for this mapping has been to minimize 
     24the amount of code needed to build a real-world domain model. This is made possible 
     25by relying on a number of conventions that make it easy for Active Record to infer 
     26complex relations and structures from a minimal amount of explicit direction. 
    2927 
    30    ...which again gives Product#name and Product#name=(new_name)  
    31     
    32   {Learn more}[link:classes/ActiveRecord/Base.html] 
    3328 
     29== Overview 
    3430 
    35 * Associations between objects controlled by simple meta-programming macros.  
     31When we access our database in Rails we ask an Active Record model to do it for us, Active Record  
     32then transforms these requests into SQL queries, executes them on the database and returns the results as  
     33either an array or a single object. 
    3634 
    37    class Firm < ActiveRecord::Base 
    38      has_many   :clients 
    39      has_one    :account 
    40      belongs_to :conglomorate 
    41    end 
     35The tables in your database are best created using the ActiveRecord::Migration class. Migrations are a way of  
     36constructing your database schema using ruby methods. They allow you to define the schema in steps  
     37which you can use to move both up and down the schema definition. This gives you an easy way to move from  
     38development to production with database changes wrapped up in easy to read ruby methods that can be executed  
     39when necessary. 
    4240 
    43   {Learn more}[link:classes/ActiveRecord/Associations/ClassMethods.html] 
     41Learn more in the ActiveRecord::Migration class file. 
    4442 
     43== Conventions 
    4544 
    46 * Aggregations of value objects controlled by simple meta-programming macros.  
     45ActiveRecord has some conventions that you should be aware of before you use it, they will clear 
     46up most queries you have about the basics. 
    4747 
    48    class Account < ActiveRecord::Base 
    49      composed_of :balance, :class_name => "Money", 
    50                  :mapping => %w(balance amount) 
    51      composed_of :address,  
    52                  :mapping => [%w(address_street street), %w(address_city city)] 
    53    end 
     48* Models should be singular, camel case and without spaces e.g. Post, Person, Comment 
     49* Table names should be plural, lower case and underscored e.g. posts, people, comments 
     50* Relationships between models are described as they would be verbally spoken e.g. has_many :posts, belongs_to :post 
     51* Foreign keys in tables are assumed to be the name of the foreign object (singular) with _id on the end e.g. if we have posts with many comments, the foreign key for comments would be post_id 
     52* Convention over configuration means that certain details are guessed, but they can be overridden if the need arises e.g. If you are working with legacy databases and the foreign keys are already set 
    5453 
    55   {Learn more}[link:classes/ActiveRecord/Aggregations/ClassMethods.html] 
     54Learn more about the methods available to your Active Record models in ActiveRecord::Base. 
    5655 
     56== Feature Examples 
    5757 
    58 * Validation rules that can differ for new or existing objects. 
     58=== Database abstraction 
    5959 
    60     class Account < ActiveRecord::Base 
    61       validates_presence_of     :subdomain, :name, :email_address, :password 
    62       validates_uniqueness_of   :subdomain 
    63       validates_acceptance_of   :terms_of_service, :on => :create 
    64       validates_confirmation_of :password, :email_address, :on => :create 
    65     end 
     60Database abstraction through simple adapters (~100 lines) with a shared connector, Rails configures the database  
     61in the environment.rb file using the details you supply in the database.yml file. 
    6662 
    67   {Learn more}[link:classes/ActiveRecord/Validations.html] 
     63  ActiveRecord::Base.establish_connection(:adapter => "sqlite", :database => "dbfile") 
    6864 
     65  ActiveRecord::Base.establish_connection( 
     66    :adapter  => "mysql",  
     67    :host     => "localhost",  
     68    :username => "me",  
     69    :password => "secret",  
     70    :database => "activerecord" 
     71  ) 
    6972 
    70 * Acts that can make records work as lists or trees: 
     73{Learn more}[link:classes/ActiveRecord/Base.html] and read about the built-in support for 
     74MySQL[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/OCIAdapter.html], SQLServer[link:classes/ActiveRecord/ConnectionAdapters/SQLServerAdapter.html], and DB2[link:classes/ActiveRecord/ConnectionAdapters/DB2Adapter.html]. 
    7175 
    72     class Item < ActiveRecord::Base 
    73       belongs_to   :list 
    74       acts_as_list :scope => :list 
    75     end 
    76      
    77     item.move_higher 
    78     item.move_to_bottom 
    7976 
    80   Learn about {acts_as_list}[link:classes/ActiveRecord/Acts/List/ClassMethods.html], {the instance methods acts_as_list provides}[link:classes/ActiveRecord/Acts/List/InstanceMethods.html], and 
    81   {acts_as_tree}[link:classes/ActiveRecord/Acts/Tree/ClassMethods.html] 
    82   
    83 * Callbacks as methods or queues on the entire lifecycle (instantiation, saving, destroying, validating, etc). 
     77=== Automated mapping 
    8478 
    85    class Person < ActiveRecord::Base 
    86      def before_destroy # is called just before Person#destroy 
    87        CreditCard.find(credit_card_id).destroy 
    88      end 
    89    end 
     79Automated mapping between your Active Record models and database tables, as well as attributes and columns. 
    9080 
    91    class Account < ActiveRecord::Base 
    92      after_find :eager_load, 'self.class.announce(#{id})' 
    93    end 
     81  class Product < ActiveRecord::Base; end 
    9482 
    95   {Learn more}[link:classes/ActiveRecord/Callbacks.html] 
     83...is automatically mapped to the table named "products", such as: 
    9684 
     85  CREATE TABLE products ( 
     86    id int(11) NOT NULL auto_increment, 
     87    name varchar(255), 
     88    PRIMARY KEY  (id) 
     89  ); 
    9790 
    98 * Observers for the entire lifecycle 
     91...which again gives Product#name and Product#name=(new_name), Active Record created the methods 
     92because of the existence of the table column "name". 
    9993 
    100    class CommentObserver < ActiveRecord::Observer 
    101      def after_create(comment) # is called just after Comment#save 
    102        Notifications.deliver_new_comment("david@loudthinking.com", comment) 
    103      end 
    104    end 
     94{Learn more}[link:classes/ActiveRecord/Base.html] 
     95     
    10596 
    106   {Learn more}[link:classes/ActiveRecord/Observer.html] 
     97=== Associations 
    10798 
     99Associations between objects controlled by simple meta-programming macros. By describing model  
     100relationships with these simple method calls we get a load of functionality for free. 
     101  
     102  class Firm < ActiveRecord::Base 
     103    has_many   :clients 
     104    has_one    :account 
     105    belongs_to :conglomerate 
     106  end 
    108107 
    109 * Inheritance hierarchies  
     108{Learn more}[link:classes/ActiveRecord/Associations/ClassMethods.html] 
    110109 
    111    class Company < ActiveRecord::Base; end 
    112    class Firm < Company; end 
    113    class Client < Company; end 
    114    class PriorityClient < Client; end 
    115110 
    116   {Learn more}[link:classes/ActiveRecord/Base.html] 
     111=== Aggregations 
    117112 
     113Aggregations of value objects controlled by simple meta-programming macros. 
    118114 
    119 * Transaction support on both a database and object level. The latter is implemented  
    120   by using Transaction::Simple[http://railsmanual.com/module/Transaction::Simple] 
     115  class Account < ActiveRecord::Base 
     116    composed_of :balance, :class_name => "Money", 
     117                :mapping => %w(balance amount) 
     118    composed_of :address,  
     119                :mapping => [%w(address_street street), %w(address_city city)] 
     120  end 
     121   
     122...which gives Account#balance and Account#address which return strings of the composed results of :mapping. 
    121123 
    122     # Just database transaction 
    123     Account.transaction do 
    124       david.withdrawal(100) 
    125       mary.deposit(100) 
    126     end 
     124{Learn more}[link:classes/ActiveRecord/Aggregations/ClassMethods.html] 
    127125 
    128     # Database and object transaction 
    129     Account.transaction(david, mary) do 
    130       david.withdrawal(100) 
    131       mary.deposit(100) 
    132     end 
    133126 
    134   {Learn more}[link:classes/ActiveRecord/Transactions/ClassMethods.html] 
     127=== Validations 
    135128 
     129Validations are a way of determining if a record contains valid details (described like the example below),  
     130if a record fails validation it will not save (unless explicitly told to skip validations). 
    136131 
    137 * Reflections on columns, associations, and aggregations 
     132Validation rules that can differ for new or existing objects depending on the options you give. 
    138133 
    139     reflection = Firm.reflect_on_association(:clients) 
    140     reflection.klass # => Client (class) 
    141     Firm.columns # Returns an array of column descriptors for the firms table 
     134  class Account < ActiveRecord::Base 
     135    validates_presence_of     :subdomain, :name, :email_address, :password 
     136    validates_uniqueness_of   :subdomain 
     137    validates_acceptance_of   :terms_of_service, :on => :create 
     138    validates_confirmation_of :password, :email_address, :on => :create 
     139  end 
    142140 
    143   {Learn more}[link:classes/ActiveRecord/Reflection/ClassMethods.html] 
     141{Learn more}[link:classes/ActiveRecord/Validations.html] 
    144142 
    145143 
    146 * Direct manipulation (instead of service invocation) 
     144=== List/Tree Behaviour 
    147145 
    148   So instead of (Hibernate[http://www.hibernate.org/] example)
     146Acts that can make records work as lists or trees
    149147 
    150      long pkId = 1234; 
    151      DomesticCat pk = (DomesticCat) sess.load( Cat.class, new Long(pkId) ); 
    152      // something interesting involving a cat... 
    153      sess.save(cat); 
    154      sess.flush(); // force the SQL INSERT 
     148    class Item < ActiveRecord::Base 
     149      belongs_to   :list 
     150      acts_as_list :scope => :list 
     151    end 
    155152 
    156   Active Record lets you: 
     153    item.move_higher 
     154    item.move_to_bottom 
    157155 
    158      pkId = 1234 
    159      cat = Cat.find(pkId) 
    160      # something even more interesting involving the same cat... 
    161      cat.save 
     156Learn about {acts_as_list}[link:classes/ActiveRecord/Acts/List/ClassMethods.html], {the instance methods acts_as_list provides}[link:classes/ActiveRecord/Acts/List/InstanceMethods.html], and {acts_as_tree}[link:classes/ActiveRecord/Acts/Tree/ClassMethods.html]. 
    162157 
    163   {Learn more}[link:classes/ActiveRecord/Base.html] 
    164158 
     159=== Callbacks 
    165160 
    166 * Database abstraction through simple adapters (~100 lines) with a shared connector 
     161Callbacks are called when certain behaviour occurs, they can happen on many different levels of the object  
     162lifecycle (instantiation, saving, destroying, validating, etc).  They can either be defined inline or given  
     163a method name (as a symbol) which will be called. 
    167164 
    168    ActiveRecord::Base.establish_connection(:adapter => "sqlite", :database => "dbfile") 
     165  class Person < ActiveRecord::Base 
     166    def before_destroy # is called just before Person#destroy 
     167      CreditCard.find(credit_card_id).destroy 
     168    end 
     169  end 
     170   
     171  class Account < ActiveRecord::Base 
     172    after_find :eager_load, 'self.class.announce(#{id})' 
     173  end 
    169174 
    170    ActiveRecord::Base.establish_connection( 
    171      :adapter  => "mysql",  
    172      :host     => "localhost",  
    173      :username => "me",  
    174      :password => "secret",  
    175      :database => "activerecord" 
    176    ) 
     175{Learn more}[link:classes/ActiveRecord/Callbacks.html] 
    177176 
    178   {Learn more}[link:classes/ActiveRecord/Base.html#M000081] and read about the built-in support for 
    179   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/OCIAdapter.html], SQLServer[link:classes/ActiveRecord/ConnectionAdapters/SQLServerAdapter.html], and DB2[link:classes/ActiveRecord/ConnectionAdapters/DB2Adapter.html]. 
    180177 
     178=== Observers 
    181179 
    182 * Logging support for Log4r[http://log4r.sourceforge.net] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc] 
     180Observers are similar to callbacks but they are written in their own class rather than directly inside the model,  
     181this allows functionality that isn't specific to the model to occur without dirtying the model with irrelevant  
     182methods.  Observers are defined in the same file location as models 
    183183 
    184     ActiveRecord::Base.logger = Logger.new(STDOUT) 
    185     ActiveRecord::Base.logger = Log4r::Logger.new("Application Log") 
     184  class CommentObserver < ActiveRecord::Observer 
     185    def after_create(comment) # is called just after Comment#save 
     186      Notifications.deliver_new_comment("david@loudthinking.com", comment) 
     187    end 
     188  end 
    186189 
     190{Learn more}[link:classes/ActiveRecord/Observer.html] 
    187191 
    188 == Simple example (1/2): Defining tables and classes (using MySQL) 
    189192 
    190 Data definitions are specified only in the database. Active Record queries the database for  
    191 the column names (that then serves to determine which attributes are valid) on regular 
    192 object instantiation through the new constructor and relies on the column names in the rows 
    193 with the finders. 
    194   
    195    # CREATE TABLE companies ( 
    196    #   id int(11) unsigned NOT NULL auto_increment, 
    197    #   client_of int(11), 
    198    #   name varchar(255), 
    199    #   type varchar(100), 
    200    #   PRIMARY KEY  (id) 
    201    # ) 
     193=== Inheritance Hierarchies 
    202194 
    203 Active Record automatically links the "Company" object to the "companies" table 
     195As with standard classes, Active Record classes can inherit from a superclass and therefore share functionality  
     196with their parent classes.  As in the example below where Car inherits from Vehicle. 
    204197 
    205    class Company < ActiveRecord::Base 
    206      has_many :people, :class_name => "Person" 
    207    end 
    208198 
    209    class Firm < Company 
    210      has_many :clients 
     199  class Vehicle < ActiveRecord::Base 
     200    belongs_to :user 
     201  end 
    211202   
    212      def people_with_all_clients 
    213       clients.inject([]) { |people, client| people + client.people } 
    214      end 
    215    end 
     203  class Car < Vehicle 
     204  end 
     205   
     206  @car = Car.find(:first) 
     207  @car.user # Functionality gained from the parent class 
    216208 
    217 The foreign_key is only necessary because we didn't use "firm_id" in the data definition 
    218   
    219    class Client < Company 
    220      belongs_to :firm, :foreign_key => "client_of" 
    221    end 
     209{Learn more}[link:classes/ActiveRecord/Base.html] 
    222210 
    223    # CREATE TABLE people ( 
    224    #   id int(11) unsigned NOT NULL auto_increment, 
    225    #   name text, 
    226    #   company_id text, 
    227    #   PRIMARY KEY  (id) 
    228    # ) 
    229211 
    230 Active Record will also automatically link the "Person" object to the "people" table 
     212=== Reflections 
    231213 
    232    class Person < ActiveRecord::Base 
    233      belongs_to :company 
    234    end 
     214Reflections give ActiveRecord the ability to determine the class and other details on columns, associations,  
     215and aggregations. 
    235216 
    236 == Simple example (2/2): Using the domain 
     217  reflection = Firm.reflect_on_association(:clients) 
     218  reflection.klass # => Client (class) 
     219  Firm.columns # Returns an array of column descriptors for the firms table 
    237220 
    238 Picking a database connection for all the Active Records 
     221{Learn more}[link:classes/ActiveRecord/Reflection/ClassMethods.html] 
    239222 
    240    ActiveRecord::Base.establish_connection( 
    241      :adapter  => "mysql",  
    242      :host     => "localhost",  
    243      :username => "me",  
    244      :password => "secret",  
    245      :database => "activerecord" 
    246    ) 
    247223 
    248 Create some fixtures 
     224=== Logging support 
    249225 
    250    firm = Firm.new("name" => "Next Angle") 
    251    # SQL: INSERT INTO companies (name, type) VALUES("Next Angle", "Firm") 
    252    firm.save 
     226Logging support for Log4r[http://log4r.sourceforge.net] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc]. 
     227Rails uses this support to add to its log files. 
    253228 
    254    client = Client.new("name" => "37signals", "client_of" => firm.id) 
    255    # SQL: INSERT INTO companies (name, client_of, type) VALUES("37signals", 1, "Firm") 
    256    client.save 
     229  ActiveRecord::Base.logger = Logger.new(STDOUT) 
     230  ActiveRecord::Base.logger = Log4r::Logger.new("Application Log") 
    257231 
    258 Lots of different finders 
    259232 
    260    # SQL: SELECT * FROM companies WHERE id = 1 
    261    next_angle = Company.find(1) 
     233== Simple Example without Rails 
    262234 
    263    # SQL: SELECT * FROM companies WHERE id = 1 AND type = 'Firm' 
    264    next_angle = Firm.find(1)     
     235Active Record determines the type of a models attribute by the database type, consider these table definitions: 
    265236 
    266    # SQL: SELECT * FROM companies WHERE id = 1 AND name = 'Next Angle' 
    267    next_angle = Company.find(:first, :conditions => "name = 'Next Angle'") 
     237  CREATE TABLE companies ( 
     238   id int(11) unsigned NOT NULL auto_increment, 
     239   client_of int(11), 
     240   name varchar(255), 
     241   type varchar(100), 
     242   PRIMARY KEY  (id) 
     243  ) 
     244   
     245  CREATE TABLE people ( 
     246   id int(11) unsigned NOT NULL auto_increment, 
     247   name text, 
     248   company_id text, 
     249   PRIMARY KEY  (id) 
     250  ) 
    268251 
    269    next_angle = Firm.find_by_sql("SELECT * FROM companies WHERE id = 1").first 
     252If we define a class of <tt>Company</tt> which inherits from ActiveRecord::Base we will have access to the  
     253attributes of that object which will map to the columns of the database table, we assume you've configured  
     254your database connection already. In the <tt>Company</tt> definition below we've told the <tt>Company</tt>  
     255model that it should create an attribute called <tt>people</tt> which is an array of <tt>Person's</tt>.   
     256We explicitly set the class name with <tt>:class_name => "Person"</tt> because the plural of people would  
     257have been assumed to be peoples, which is undesirable in this case. 
    270258 
    271 The supertype, Company, will return subtype instances 
     259  class Company < ActiveRecord::Base 
     260    has_many :people, :class_name => "Person" 
     261  end 
     262   
     263  class Person < ActiveRecord::Base 
     264    belongs_to :company 
     265  end 
     266   
     267With these simple definitions we can now automatically access all the people in a company like so: 
    272268 
    273    Firm === next_angle 
     269  company = Company.find(:first) 
     270  first_person = company.people.first # gives us the first Person object 
     271   
     272There are more examples in ActiveRecord::Base and other child classes. 
    274273 
    275 All the dynamic methods added by the has_many macro 
    276  
    277   next_angle.clients.empty?  # true 
    278   next_angle.clients.size    # total number of clients 
    279   all_clients = next_angle.clients 
    280  
    281 Constrained finds makes access security easier when ID comes from a web-app 
    282  
    283    # SQL: SELECT * FROM companies WHERE client_of = 1 AND type = 'Client' AND id = 2 
    284    thirty_seven_signals = next_angle.clients.find(2) 
    285  
    286 Bi-directional associations thanks to the "belongs_to" macro 
    287  
    288    thirty_seven_signals.firm.nil? # true 
    289  
    290  
    291 == Examples 
    292  
    293 Active Record ships with a couple of examples that should give you a good feel for 
    294 operating usage. Be sure to edit the <tt>examples/shared_setup.rb</tt> file for your 
    295 own database before running the examples. Possibly also the table definition SQL in  
    296 the examples themselves. 
    297  
    298 It's also highly recommended to have a look at the unit tests. Read more in link:files/RUNNING_UNIT_TESTS.html 
    299  
    300  
    301 == Philosophy  
    302  
    303 Active Record attempts to provide a coherent wrapper as a solution for the inconvenience that is  
    304 object-relational mapping. The prime directive for this mapping has been to minimize 
    305 the amount of code needed to build a real-world domain model. This is made possible 
    306 by relying on a number of conventions that make it easy for Active Record to infer 
    307 complex relations and structures from a minimal amount of explicit direction. 
    308  
    309 Convention over Configuration: 
    310 * No XML-files! 
    311 * Lots of reflection and run-time extension 
    312 * Magic is not inherently a bad word  
    313  
    314 Admit the Database: 
    315 * Lets you drop down to SQL for odd cases and performance 
    316 * Doesn't attempt to duplicate or replace data definitions 
    317  
    318  
    319274== Download 
    320275 
    321276The latest version of Active Record can be found at 
     
    333288RubyGems[http://rubygems.rubyforge.org/wiki/wiki.pl] installed for that, though. If you have, 
    334289then use: 
    335290 
    336   % [sudo] gem install activerecord-1.10.0.gem 
     291  % [sudo] gem install activerecord-1.15.2.gem 
    337292 
    338293You can also install Active Record the old-fashion way with the following command: 
    339294