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

root/branches/2-1-caching/activerecord/README

Revision 8371, 11.1 kB (checked in by marcel, 1 year ago)

Remove references to ActsAs* from the README now that it's been pulled out into a plugin. Closes #10451 [sjgman9]

  • 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 * Transaction support on both a database and object level. The latter is implemented
106   by using Transaction::Simple[http://railsmanual.com/module/Transaction::Simple]
107
108     # Just database transaction
109     Account.transaction do
110       david.withdrawal(100)
111       mary.deposit(100)
112     end
113
114     # Database and object transaction
115     Account.transaction(david, mary) do
116       david.withdrawal(100)
117       mary.deposit(100)
118     end
119
120   {Learn more}[link:classes/ActiveRecord/Transactions/ClassMethods.html]
121
122
123 * Reflections on columns, associations, and aggregations
124
125     reflection = Firm.reflect_on_association(:clients)
126     reflection.klass # => Client (class)
127     Firm.columns # Returns an array of column descriptors for the firms table
128
129   {Learn more}[link:classes/ActiveRecord/Reflection/ClassMethods.html]
130
131
132 * Direct manipulation (instead of service invocation)
133
134   So instead of (Hibernate[http://www.hibernate.org/] example):
135
136      long pkId = 1234;
137      DomesticCat pk = (DomesticCat) sess.load( Cat.class, new Long(pkId) );
138      // something interesting involving a cat...
139      sess.save(cat);
140      sess.flush(); // force the SQL INSERT
141
142   Active Record lets you:
143
144      pkId = 1234
145      cat = Cat.find(pkId)
146      # something even more interesting involving the same cat...
147      cat.save
148
149   {Learn more}[link:classes/ActiveRecord/Base.html]
150
151
152 * Database abstraction through simple adapters (~100 lines) with a shared connector
153
154    ActiveRecord::Base.establish_connection(:adapter => "sqlite", :database => "dbfile")
155
156    ActiveRecord::Base.establish_connection(
157      :adapter  => "mysql",
158      :host     => "localhost",
159      :username => "me",
160      :password => "secret",
161      :database => "activerecord"
162    )
163
164   {Learn more}[link:classes/ActiveRecord/Base.html#M000081] and read about the built-in support for
165   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].
166
167
168 * Logging support for Log4r[http://log4r.sourceforge.net] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc]
169
170     ActiveRecord::Base.logger = Logger.new(STDOUT)
171     ActiveRecord::Base.logger = Log4r::Logger.new("Application Log")
172
173
174 == Simple example (1/2): Defining tables and classes (using MySQL)
175
176 Data definitions are specified only in the database. Active Record queries the database for
177 the column names (that then serves to determine which attributes are valid) on regular
178 object instantiation through the new constructor and relies on the column names in the rows
179 with the finders.
180  
181    # CREATE TABLE companies (
182    #   id int(11) unsigned NOT NULL auto_increment,
183    #   client_of int(11),
184    #   name varchar(255),
185    #   type varchar(100),
186    #   PRIMARY KEY  (id)
187    # )
188
189 Active Record automatically links the "Company" object to the "companies" table
190
191    class Company < ActiveRecord::Base
192      has_many :people, :class_name => "Person"
193    end
194
195    class Firm < Company
196      has_many :clients
197  
198      def people_with_all_clients
199       clients.inject([]) { |people, client| people + client.people }
200      end
201    end
202
203 The foreign_key is only necessary because we didn't use "firm_id" in the data definition
204  
205    class Client < Company
206      belongs_to :firm, :foreign_key => "client_of"
207    end
208
209    # CREATE TABLE people (
210    #   id int(11) unsigned NOT NULL auto_increment,
211    #   name text,
212    #   company_id text,
213    #   PRIMARY KEY  (id)
214    # )
215
216 Active Record will also automatically link the "Person" object to the "people" table
217
218    class Person < ActiveRecord::Base
219      belongs_to :company
220    end
221
222 == Simple example (2/2): Using the domain
223
224 Picking a database connection for all the Active Records
225
226    ActiveRecord::Base.establish_connection(
227      :adapter  => "mysql",
228      :host     => "localhost",
229      :username => "me",
230      :password => "secret",
231      :database => "activerecord"
232    )
233
234 Create some fixtures
235
236    firm = Firm.new("name" => "Next Angle")
237    # SQL: INSERT INTO companies (name, type) VALUES("Next Angle", "Firm")
238    firm.save
239
240    client = Client.new("name" => "37signals", "client_of" => firm.id)
241    # SQL: INSERT INTO companies (name, client_of, type) VALUES("37signals", 1, "Firm")
242    client.save
243
244 Lots of different finders
245
246    # SQL: SELECT * FROM companies WHERE id = 1
247    next_angle = Company.find(1)
248
249    # SQL: SELECT * FROM companies WHERE id = 1 AND type = 'Firm'
250    next_angle = Firm.find(1)   
251
252    # SQL: SELECT * FROM companies WHERE id = 1 AND name = 'Next Angle'
253    next_angle = Company.find(:first, :conditions => "name = 'Next Angle'")
254
255    next_angle = Firm.find_by_sql("SELECT * FROM companies WHERE id = 1").first
256
257 The supertype, Company, will return subtype instances
258
259    Firm === next_angle
260
261 All the dynamic methods added by the has_many macro
262
263   next_angle.clients.empty?  # true
264   next_angle.clients.size    # total number of clients
265   all_clients = next_angle.clients
266
267 Constrained finds makes access security easier when ID comes from a web-app
268
269    # SQL: SELECT * FROM companies WHERE client_of = 1 AND type = 'Client' AND id = 2
270    thirty_seven_signals = next_angle.clients.find(2)
271
272 Bi-directional associations thanks to the "belongs_to" macro
273
274    thirty_seven_signals.firm.nil? # true
275
276
277 == Examples
278
279 Active Record ships with a couple of examples that should give you a good feel for
280 operating usage. Be sure to edit the <tt>examples/shared_setup.rb</tt> file for your
281 own database before running the examples. Possibly also the table definition SQL in
282 the examples themselves.
283
284 It's also highly recommended to have a look at the unit tests. Read more in link:files/RUNNING_UNIT_TESTS.html
285
286
287 == Philosophy
288
289 Active Record attempts to provide a coherent wrapper as a solution for the inconvenience that is
290 object-relational mapping. The prime directive for this mapping has been to minimize
291 the amount of code needed to build a real-world domain model. This is made possible
292 by relying on a number of conventions that make it easy for Active Record to infer
293 complex relations and structures from a minimal amount of explicit direction.
294
295 Convention over Configuration:
296 * No XML-files!
297 * Lots of reflection and run-time extension
298 * Magic is not inherently a bad word
299
300 Admit the Database:
301 * Lets you drop down to SQL for odd cases and performance
302 * Doesn't attempt to duplicate or replace data definitions
303
304
305 == Download
306
307 The latest version of Active Record can be found at
308
309 * http://rubyforge.org/project/showfiles.php?group_id=182
310
311 Documentation can be found at
312
313 * http://ar.rubyonrails.com
314
315
316 == Installation
317
318 The prefered method of installing Active Record is through its GEM file. You'll need to have
319 RubyGems[http://rubygems.rubyforge.org/wiki/wiki.pl] installed for that, though. If you have,
320 then use:
321
322   % [sudo] gem install activerecord-1.10.0.gem
323
324 You can also install Active Record the old-fashion way with the following command:
325
326   % [sudo] ruby install.rb
327
328 from its distribution directory.
329
330
331 == License
332
333 Active Record is released under the MIT license.
334
335
336 == Support
337
338 The Active Record homepage is http://www.rubyonrails.com. You can find the Active Record
339 RubyForge page at http://rubyforge.org/projects/activerecord. And as Jim from Rake says:
340
341    Feel free to submit commits or feature requests.  If you send a patch,
342    remember to update the corresponding unit tests.  If fact, I prefer
343    new feature to be submitted in the form of new unit tests.
344
345 For other information, feel free to ask on the ruby-talk mailing list
346 (which is mirrored to comp.lang.ruby) or contact mailto:david@loudthinking.com.
Note: See TracBrowser for help on using the browser.