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

root/trunk/activerecord/README

Revision 9226, 10.8 kB (checked in by pratik, 8 months ago)

Improve documentation.

  • Property svn:executable set to *
Line 
1 = Active Record -- Object-relation mapping put on rails
2
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]
6 by the same name as described by Martin Fowler:
7
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."
10
11 Active Record's main contribution to the pattern is to relieve the original of two stunting problems:
12 lack of associations and inheritance. By adding a simple domain language-like set of macros to describe
13 the former and integrating the Single Table Inheritance pattern for the latter, Active Record narrows the
14 gap of functionality between the data mapper and active record approach.
15
16 A short rundown of the major features:
17
18 * Automated mapping between classes and tables, attributes and columns.
19
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    );
29
30    ...which again gives Product#name and Product#name=(new_name)
31    
32   {Learn more}[link:classes/ActiveRecord/Base.html]
33
34
35 * Associations between objects controlled by simple meta-programming macros.
36
37    class Firm < ActiveRecord::Base
38      has_many   :clients
39      has_one    :account
40      belongs_to :conglomorate
41    end
42
43   {Learn more}[link:classes/ActiveRecord/Associations/ClassMethods.html]
44
45
46 * Aggregations of value objects controlled by simple meta-programming macros.
47
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
54
55   {Learn more}[link:classes/ActiveRecord/Aggregations/ClassMethods.html]
56
57
58 * Validation rules that can differ for new or existing objects.
59
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
66
67   {Learn more}[link:classes/ActiveRecord/Validations.html]
68  
69 * Callbacks as methods or queues on the entire lifecycle (instantiation, saving, destroying, validating, etc).
70
71    class Person < ActiveRecord::Base
72      def before_destroy # is called just before Person#destroy
73        CreditCard.find(credit_card_id).destroy
74      end
75    end
76
77    class Account < ActiveRecord::Base
78      after_find :eager_load, 'self.class.announce(#{id})'
79    end
80
81   {Learn more}[link:classes/ActiveRecord/Callbacks.html]
82
83
84 * Observers for the entire lifecycle
85
86    class CommentObserver < ActiveRecord::Observer
87      def after_create(comment) # is called just after Comment#save
88        Notifications.deliver_new_comment("david@loudthinking.com", comment)
89      end
90    end
91
92   {Learn more}[link:classes/ActiveRecord/Observer.html]
93
94
95 * Inheritance hierarchies
96
97    class Company < ActiveRecord::Base; end
98    class Firm < Company; end
99    class Client < Company; end
100    class PriorityClient < Client; end
101
102   {Learn more}[link:classes/ActiveRecord/Base.html]
103
104
105 * Transactions
106
107     # Database transaction
108     Account.transaction do
109       david.withdrawal(100)
110       mary.deposit(100)
111     end
112
113   {Learn more}[link:classes/ActiveRecord/Transactions/ClassMethods.html]
114
115
116 * Reflections on columns, associations, and aggregations
117
118     reflection = Firm.reflect_on_association(:clients)
119     reflection.klass # => Client (class)
120     Firm.columns # Returns an array of column descriptors for the firms table
121
122   {Learn more}[link:classes/ActiveRecord/Reflection/ClassMethods.html]
123
124
125 * Direct manipulation (instead of service invocation)
126
127   So instead of (Hibernate[http://www.hibernate.org/] example):
128
129      long pkId = 1234;
130      DomesticCat pk = (DomesticCat) sess.load( Cat.class, new Long(pkId) );
131      // something interesting involving a cat...
132      sess.save(cat);
133      sess.flush(); // force the SQL INSERT
134
135   Active Record lets you:
136
137      pkId = 1234
138      cat = Cat.find(pkId)
139      # something even more interesting involving the same cat...
140      cat.save
141
142   {Learn more}[link:classes/ActiveRecord/Base.html]
143
144
145 * Database abstraction through simple adapters (~100 lines) with a shared connector
146
147    ActiveRecord::Base.establish_connection(:adapter => "sqlite", :database => "dbfile")
148
149    ActiveRecord::Base.establish_connection(
150      :adapter  => "mysql",
151      :host     => "localhost",
152      :username => "me",
153      :password => "secret",
154      :database => "activerecord"
155    )
156
157   {Learn more}[link:classes/ActiveRecord/Base.html#M000081] and read about the built-in support for
158   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].
159
160
161 * Logging support for Log4r[http://log4r.sourceforge.net] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc]
162
163     ActiveRecord::Base.logger = Logger.new(STDOUT)
164     ActiveRecord::Base.logger = Log4r::Logger.new("Application Log")
165
166
167 == Simple example (1/2): Defining tables and classes (using MySQL)
168
169 Data definitions are specified only in the database. Active Record queries the database for
170 the column names (that then serves to determine which attributes are valid) on regular
171 object instantiation through the new constructor and relies on the column names in the rows
172 with the finders.
173  
174    # CREATE TABLE companies (
175    #   id int(11) unsigned NOT NULL auto_increment,
176    #   client_of int(11),
177    #   name varchar(255),
178    #   type varchar(100),
179    #   PRIMARY KEY  (id)
180    # )
181
182 Active Record automatically links the "Company" object to the "companies" table
183
184    class Company < ActiveRecord::Base
185      has_many :people, :class_name => "Person"
186    end
187
188    class Firm < Company
189      has_many :clients
190  
191      def people_with_all_clients
192       clients.inject([]) { |people, client| people + client.people }
193      end
194    end
195
196 The foreign_key is only necessary because we didn't use "firm_id" in the data definition
197  
198    class Client < Company
199      belongs_to :firm, :foreign_key => "client_of"
200    end
201
202    # CREATE TABLE people (
203    #   id int(11) unsigned NOT NULL auto_increment,
204    #   name text,
205    #   company_id text,
206    #   PRIMARY KEY  (id)
207    # )
208
209 Active Record will also automatically link the "Person" object to the "people" table
210
211    class Person < ActiveRecord::Base
212      belongs_to :company
213    end
214
215 == Simple example (2/2): Using the domain
216
217 Picking a database connection for all the Active Records
218
219    ActiveRecord::Base.establish_connection(
220      :adapter  => "mysql",
221      :host     => "localhost",
222      :username => "me",
223      :password => "secret",
224      :database => "activerecord"
225    )
226
227 Create some fixtures
228
229    firm = Firm.new("name" => "Next Angle")
230    # SQL: INSERT INTO companies (name, type) VALUES("Next Angle", "Firm")
231    firm.save
232
233    client = Client.new("name" => "37signals", "client_of" => firm.id)
234    # SQL: INSERT INTO companies (name, client_of, type) VALUES("37signals", 1, "Firm")
235    client.save
236
237 Lots of different finders
238
239    # SQL: SELECT * FROM companies WHERE id = 1
240    next_angle = Company.find(1)
241
242    # SQL: SELECT * FROM companies WHERE id = 1 AND type = 'Firm'
243    next_angle = Firm.find(1)   
244
245    # SQL: SELECT * FROM companies WHERE id = 1 AND name = 'Next Angle'
246    next_angle = Company.find(:first, :conditions => "name = 'Next Angle'")
247
248    next_angle = Firm.find_by_sql("SELECT * FROM companies WHERE id = 1").first
249
250 The supertype, Company, will return subtype instances
251
252    Firm === next_angle
253
254 All the dynamic methods added by the has_many macro
255
256   next_angle.clients.empty?  # true
257   next_angle.clients.size    # total number of clients
258   all_clients = next_angle.clients
259
260 Constrained finds makes access security easier when ID comes from a web-app
261
262    # SQL: SELECT * FROM companies WHERE client_of = 1 AND type = 'Client' AND id = 2
263    thirty_seven_signals = next_angle.clients.find(2)
264
265 Bi-directional associations thanks to the "belongs_to" macro
266
267    thirty_seven_signals.firm.nil? # true
268
269
270 == Examples
271
272 Active Record ships with a couple of examples that should give you a good feel for
273 operating usage. Be sure to edit the <tt>examples/shared_setup.rb</tt> file for your
274 own database before running the examples. Possibly also the table definition SQL in
275 the examples themselves.
276
277 It's also highly recommended to have a look at the unit tests. Read more in link:files/RUNNING_UNIT_TESTS.html
278
279
280 == Philosophy
281
282 Active Record attempts to provide a coherent wrapper as a solution for the inconvenience that is
283 object-relational mapping. The prime directive for this mapping has been to minimize
284 the amount of code needed to build a real-world domain model. This is made possible
285 by relying on a number of conventions that make it easy for Active Record to infer
286 complex relations and structures from a minimal amount of explicit direction.
287
288 Convention over Configuration:
289 * No XML-files!
290 * Lots of reflection and run-time extension
291 * Magic is not inherently a bad word
292
293 Admit the Database:
294 * Lets you drop down to SQL for odd cases and performance
295 * Doesn't attempt to duplicate or replace data definitions
296
297
298 == Download
299
300 The latest version of Active Record can be found at
301
302 * http://rubyforge.org/project/showfiles.php?group_id=182
303
304 Documentation can be found at
305
306 * http://ar.rubyonrails.com
307
308
309 == Installation
310
311 The prefered method of installing Active Record is through its GEM file. You'll need to have
312 RubyGems[http://rubygems.rubyforge.org/wiki/wiki.pl] installed for that, though. If you have,
313 then use:
314
315   % [sudo] gem install activerecord-1.10.0.gem
316
317 You can also install Active Record the old-fashion way with the following command:
318
319   % [sudo] ruby install.rb
320
321 from its distribution directory.
322
323
324 == License
325
326 Active Record is released under the MIT license.
327
328
329 == Support
330
331 The Active Record homepage is http://www.rubyonrails.com. You can find the Active Record
332 RubyForge page at http://rubyforge.org/projects/activerecord. And as Jim from Rake says:
333
334    Feel free to submit commits or feature requests.  If you send a patch,
335    remember to update the corresponding unit tests.  If fact, I prefer
336    new feature to be submitted in the form of new unit tests.
337
338 For other information, feel free to ask on the ruby-talk mailing list
339 (which is mirrored to comp.lang.ruby) or contact mailto:david@loudthinking.com.
Note: See TracBrowser for help on using the browser.