nitro 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. data/AUTHORS +4 -1
  2. data/ChangeLog +290 -7
  3. data/README +3 -3
  4. data/RELEASES +94 -0
  5. data/Rakefile +7 -7
  6. data/benchmark/og/bench.rb +11 -10
  7. data/{ChangeLog.1 → doc/ChangeLog.1} +0 -0
  8. data/doc/apache.txt +9 -0
  9. data/doc/architecture.txt +1 -27
  10. data/doc/bugs.txt +11 -4
  11. data/doc/config.txt +22 -0
  12. data/doc/og_config.txt +35 -0
  13. data/doc/og_tutorial.txt +595 -0
  14. data/doc/tutorial.txt +22 -0
  15. data/examples/blog/conf/apache.conf +30 -0
  16. data/examples/blog/conf/lhttpd.conf +2 -2
  17. data/examples/blog/lib/blog/controller.rb +11 -2
  18. data/examples/blog/log/apache.error_log +5528 -0
  19. data/examples/blog/root/fcgi.rb +1 -1
  20. data/examples/blog/run.rb +9 -3
  21. data/examples/flash/run.rb +2 -2
  22. data/examples/no_xsl_blog/conf/apache.conf +30 -0
  23. data/examples/no_xsl_blog/conf/lhttpd.conf +1 -1
  24. data/examples/no_xsl_blog/lib/blog/controller.rb +2 -2
  25. data/examples/no_xsl_blog/log/apache.error_log +68 -0
  26. data/examples/no_xsl_blog/root/fcgi.rb +2 -2
  27. data/examples/no_xsl_blog/run.rb +3 -3
  28. data/examples/og/run.rb +1 -1
  29. data/examples/tiny/conf/apache.conf +29 -4
  30. data/examples/tiny/conf/lhttpd.conf +1 -1
  31. data/examples/tiny/log/apache.error_log +30 -0
  32. data/examples/tiny/root/fcgi.rb +2 -2
  33. data/examples/tiny/root/index.xhtml +1 -1
  34. data/examples/tiny/run.rb +3 -2
  35. data/examples/wee_style/run.rb +7 -9
  36. data/examples/why_wiki/README +5 -0
  37. data/examples/why_wiki/run.rb +57 -0
  38. data/examples/why_wiki/wiki.yml +6 -0
  39. data/examples/wiki.yml +1 -0
  40. data/lib/glue/array.rb +14 -33
  41. data/lib/glue/hash.rb +32 -53
  42. data/lib/glue/pool.rb +9 -12
  43. data/lib/glue/property.rb +31 -9
  44. data/lib/nitro.rb +30 -8
  45. data/lib/nitro/adapters/cgi.rb +23 -3
  46. data/lib/nitro/adapters/webrick.rb +9 -3
  47. data/lib/nitro/builders/form.rb +21 -13
  48. data/lib/nitro/builders/rss.rb +20 -9
  49. data/lib/nitro/builders/table.rb +69 -10
  50. data/lib/nitro/builders/xhtml.rb +13 -4
  51. data/lib/nitro/component.rb +15 -0
  52. data/lib/nitro/conf.rb +4 -3
  53. data/lib/nitro/context.rb +22 -14
  54. data/lib/nitro/controller.rb +63 -5
  55. data/lib/nitro/dispatcher.rb +11 -6
  56. data/lib/nitro/output.rb +28 -0
  57. data/lib/nitro/render.rb +78 -59
  58. data/lib/nitro/request.rb +5 -1
  59. data/lib/nitro/runner.rb +20 -6
  60. data/lib/nitro/session.rb +89 -18
  61. data/lib/nitro/session/drb.rb +31 -0
  62. data/lib/nitro/session/drbserver.rb +71 -0
  63. data/lib/nitro/session/memory.rb +13 -0
  64. data/lib/nitro/simple.rb +7 -0
  65. data/lib/nitro/ui/date-select.rb +2 -5
  66. data/lib/nitro/ui/pager.rb +4 -4
  67. data/lib/nitro/ui/tabs.rb +2 -4
  68. data/lib/nitro/uri.rb +7 -4
  69. data/lib/og.rb +20 -12
  70. data/lib/og/adapter.rb +40 -13
  71. data/lib/og/adapters/filesys.rb +121 -0
  72. data/lib/og/adapters/mysql.rb +10 -5
  73. data/lib/og/adapters/oracle.rb +374 -0
  74. data/lib/og/adapters/psql.rb +10 -23
  75. data/lib/og/adapters/sqlite.rb +3 -3
  76. data/lib/og/backend.rb +2 -2
  77. data/lib/og/connection.rb +6 -6
  78. data/lib/og/database.rb +5 -5
  79. data/lib/og/enchant.rb +6 -2
  80. data/lib/og/meta.rb +56 -26
  81. data/lib/og/mock.rb +1 -1
  82. data/lib/og/typemacros.rb +23 -0
  83. data/lib/parts/content.rb +4 -10
  84. data/test/nitro/adapters/tc_cgi.rb +1 -1
  85. data/test/nitro/builders/tc_rss.rb +1 -1
  86. data/test/nitro/builders/tc_table.rb +30 -0
  87. data/test/nitro/tc_context.rb +4 -0
  88. data/test/nitro/tc_controller.rb +9 -2
  89. data/test/og/tc_filesys.rb +83 -0
  90. data/test/og/tc_meta.rb +55 -0
  91. data/test/tc_og.rb +115 -36
  92. data/vendor/README +11 -5
  93. data/vendor/breakpoint.rb +35 -38
  94. data/vendor/breakpoint_client.rb +119 -80
  95. data/vendor/composite_sexp_processor.rb +43 -0
  96. data/vendor/parse_tree.rb +745 -0
  97. data/vendor/sexp_processor.rb +453 -0
  98. metadata +34 -7
  99. data/examples/no_xsl_blog/conf/app.conf.rb +0 -47
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'rake/rdoctask'
6
6
  require 'rake/testtask'
7
7
  require 'rake/gempackagetask'
8
8
 
9
- og = false
9
+ og = false
10
10
 
11
11
  task :default => :package
12
12
 
@@ -14,7 +14,7 @@ task :default => :package
14
14
 
15
15
  Rake::TestTask.new do |t|
16
16
  t.libs << 'test'
17
- t.test_files = FileList['test/**/tc*.rb'].exclude('**/tc*og*.rb')
17
+ t.test_files = FileList['test/**/tc*.rb'].exclude('**/tc*og*.rb').exclude('test/og/**/*')
18
18
  t.verbose = true
19
19
  end
20
20
 
@@ -30,8 +30,8 @@ end
30
30
 
31
31
  Rake::RDocTask.new do |rd|
32
32
  rd.main = 'README'
33
- rd.rdoc_dir = 'rdoc'
34
- rd.rdoc_files.include('README', 'INSTALL', 'lib/**/*.rb')
33
+ rd.rdoc_dir = 'doc/rdoc'
34
+ rd.rdoc_files.include('README', 'INSTALL', 'doc/og_config.txt', 'doc/og_tutorial.txt', 'lib/**/*.rb')
35
35
  rd.options << '--all --inline-source'
36
36
  end
37
37
 
@@ -91,11 +91,11 @@ spec = Gem::Specification.new do |s|
91
91
  s.summary = 'Nitro Web Engine'
92
92
  s.description =
93
93
  'An efficient, multiparadigm and flexible platform for rapid ' +
94
- 'web application development. Implements a full development stack ' +
95
- '(Model, View, Controller).'
96
-
94
+ 'web application development. Implements a full development stack.'
95
+
97
96
  # s.add_dependency 'postgres-pr', '>= 0.3.0'
98
97
  # s.add_dependency 'postgres', '>= 0.7.1'
98
+ # s.add_dependency 'ParseTree', '>= 1.3.3'
99
99
  s.add_dependency 'extensions', '>= 0.5'
100
100
  # s.add_dependency 'sqlite3-ruby', '>= 1.0.0'
101
101
  # s.add_dependency 'mysql', '>= 2.5.1'
@@ -1,12 +1,9 @@
1
1
  # * George Moschovitis <gm@navel.gr>
2
2
  # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: bench.rb 255 2005-02-10 12:45:32Z gmosx $
3
+ # $Id: bench.rb 263 2005-02-23 13:45:08Z gmosx $
4
4
 
5
- $:.unshift File.join(File.dirname(__FILE__), '..', '..', 'lib')
5
+ require 'og'; include Og
6
6
 
7
- require 'og'
8
-
9
- GC.disable
10
7
 
11
8
  config = {
12
9
  :adapter => 'sqlite',
@@ -28,8 +25,8 @@ class Article
28
25
  end
29
26
  end
30
27
 
31
- Og::Database.drop_db!(config)
32
- db = Og::Database.new(config)
28
+ Database.drop_db!(config)
29
+ db = Database.new(config)
33
30
 
34
31
  # Benchmark the insert speed. Useful for finding
35
32
  # the improvement when using prepared statements.
@@ -44,6 +41,8 @@ sum = 0
44
41
  min = 999999
45
42
  max = -min
46
43
 
44
+ GC.disable
45
+
47
46
  10.times do |i|
48
47
 
49
48
  db.exec "DELETE FROM #{Article::DBTABLE}"
@@ -69,6 +68,8 @@ max = -min
69
68
 
70
69
  end
71
70
 
72
- puts "Min: #{min}"
73
- puts "Max: #{max}"
74
- puts "Average: #{sum/10}"
71
+ puts %{
72
+ Min: #{min}
73
+ Max: #{max}
74
+ Average: #{sum/10}
75
+ }
File without changes
@@ -0,0 +1,9 @@
1
+ = Apache
2
+
3
+ Nitro runs under the Apache Web Server using FastCGI.
4
+
5
+ == Setup
6
+
7
+ Download and setup the latest Apache distribution:
8
+
9
+ Download and setup the latest mod_fcgi distirbution:
@@ -1,28 +1,2 @@
1
+ = Architecture
1
2
 
2
-
3
-
4
- Client:
5
- Web Browsers, Mobile Phones, PDA, TV
6
-
7
- HttpServer:
8
- handles .html, .gif/.jpg/.png, .css, .js, .swf, .ssi
9
- no loging, tuned for maximum performance, clustered
10
-
11
- AppServer:
12
- handles dynamic pages, controller / actions (.sx, .si, .ss, .sc)
13
- loging, clustered
14
-
15
- Cluster:
16
- make distributed by hash! (all servers have access to all stateservers)
17
- and multiplex access.
18
-
19
- SyncServer:
20
- multiple servers for multiple services
21
-
22
- DBServer:
23
- Database
24
- Clustered (rdbms)
25
-
26
- DNSServer:
27
- Provides dns services, initial clustering support.
28
- (inherently clustered)
@@ -1,7 +1,14 @@
1
+ = Bugs
1
2
 
2
- IMPORTANT:
3
+ === Important
3
4
 
4
- SMALL:
5
+ * multipart handling in fcgi adapter.
6
+
7
+ === Security
8
+
9
+ * you can get the xthml files in the WEBrick adapter.
10
+
11
+ === Small
12
+
13
+ * not correct content type in blog syndication.
5
14
 
6
- - the script filenames contain '//' path should not contain
7
- leading '/' ??
@@ -0,0 +1,22 @@
1
+ = Configuration parameters
2
+
3
+ This file presents a complete list of Nitro Configuration
4
+ Parameters.
5
+
6
+ === Session.store_type = :memory
7
+
8
+ Selects the mechanism employed for storing the sessions.
9
+ Available values are:
10
+
11
+ [+:memory+]
12
+ This is the default mechanism, sessions are stored
13
+ in memory. Only useful in multithreaded environments
14
+ like WEBrick.
15
+
16
+ [+:drb+]
17
+ Distributed Sessions using DRb. An independed DRb
18
+ server stores the sessions.
19
+
20
+ In the future there will be more options available (:memcache,
21
+ :filesys, :db)
22
+
@@ -0,0 +1,35 @@
1
+ = Og Configuration parameters
2
+
3
+ This file presents a complete list of Og configuration
4
+ parameters.
5
+
6
+ === Og.table_prefix = nil
7
+
8
+ Attach the given prefix to all generated SQL table names.
9
+ Usefull on hosting scenarios where you have to run multiple
10
+ applications/sites on a single database.
11
+
12
+ === Og.auto_manage_classes = true
13
+
14
+ If true, use Ruby's advanced introspection capabilities to
15
+ automatically manage classes that define properties.
16
+
17
+ === Og.enchant_managed_classes = true
18
+
19
+ If true, the library automatically 'enchants' managed classes.
20
+ In enchant mode, special db aware methods are added to
21
+ managed classes and instances. If false, Og enchants only classes
22
+ that define properties.
23
+
24
+ === Og.create_schema = true
25
+
26
+ If set to true, Og attempts to recreate the database schema
27
+ to store the managed objects. If the table for an object class
28
+ exists, Og does not recreate it. It is useful to get Og to
29
+ automatically create the schema for your application on setup,
30
+ or when you add a new managed class to your application. On
31
+ the downside, it inflicts a longer startup time on the
32
+ application, so it is better to set this to false in
33
+ production / live environments.
34
+
35
+ This option is by default true to facilitate easy development.
@@ -0,0 +1,595 @@
1
+ = Og Tutorial
2
+
3
+ === Introduction
4
+
5
+ Ruby is a wonderful object oriented language featuring a well
6
+ designed syntax and advanced constructs to bring the joy back
7
+ to programming. Creating the object model to describe your
8
+ problem domain is easy, but making this model persistent is
9
+ another story: you have to deal with relational databases
10
+ and the SQL language.
11
+
12
+ RDBMS systems are a proven and robust technology for storing
13
+ and querying data, but after experiencing the wonders of Ruby,
14
+ it is hard not to wish for a better way to integrate the OOP
15
+ and Relational paradigms.
16
+
17
+ Og makes your dream come true! Og stands for ObjectGraph and
18
+ provides a transparent way to make your objects persistent
19
+ while leveraging the full querying power of an RDBMS system.
20
+ In fact, Og is designed to use an RDBMS system like MySQL or
21
+ PostgreSQL to implement the actual data store where the
22
+ objects are serialized.
23
+
24
+ But, enough with the techno-babble, let's walk through a simple
25
+ example to give you a better idea of what Og can do.
26
+
27
+
28
+ === Installing Og
29
+
30
+ The best way to install Og is through RubyGems. For example:
31
+
32
+ gem install og
33
+
34
+ In order to use Og with a specific RDBMS, you have to install
35
+ the corresponding Ruby binding. A list of supported RDBMS's
36
+ and information about the Ruby bindings can be found in the
37
+ README file.
38
+
39
+ Alternatively, you can install a .tar.gz or .zip distribution.
40
+ You can find these at the following URL:
41
+
42
+ http://www.rubyforge.com/projects/nitro
43
+
44
+
45
+ === A Basic Blog Model
46
+
47
+ Blogs are in vogue. It seems that almost everyone is running a blog, and
48
+ many try to code one from scratch. We'll review the steps necessary
49
+ to generate the persistence model for a blog application using Og.
50
+
51
+ Let's start by designing the objects we'll use. Our simple Blog
52
+ will use these three objects:
53
+
54
+ # Blog category
55
+
56
+ class Category
57
+ attr_accessor :name
58
+ end
59
+
60
+ # Blog posting
61
+
62
+ class Post
63
+ attr_accessor :title
64
+ attr_accessor :body
65
+ attr_accessor :author
66
+ end
67
+
68
+ # Blog comment
69
+
70
+ class Comment
71
+ attr_accessor :title
72
+ attr_accessor :body
73
+ attr_accessor :author
74
+ end
75
+
76
+ As you can see, this is pure Ruby code. One of the features of
77
+ Ruby is dynamic typing. When defining the attributes of our
78
+ objects, we don't declare the actual type. However, in order to
79
+ persist the model in SQL, we need to provide some hints to Og.
80
+
81
+ Og provides a replacement to the attr* family of methods to
82
+ facilitate attaching metadata to the object's attributes. An
83
+ attribute that contains metadata is called a property. For
84
+ each attr* method, there is a corresponding prop* method. That is,
85
+
86
+ attr => prop
87
+ attr_accessor => prop_accessor
88
+ attr_reader => prop_reader
89
+ attr_writer => prop_writer
90
+
91
+ Here are the class definitions using the property mechanism:
92
+
93
+ require 'og'
94
+
95
+ class Category
96
+ prop_accessor :name, String
97
+ end
98
+
99
+ class Post
100
+ prop_accessor :title, String
101
+ prop_accessor :body, String
102
+ prop_accessor :author, String
103
+ prop_accessor :create_time, Time
104
+ prop_accessor :hits, Fixnum
105
+ end
106
+
107
+ class Comment
108
+ prop_accessor :title, String
109
+ prop_accessor :body, String
110
+ prop_accessor :author, String
111
+ prop_accessor :create_time, Time
112
+ end
113
+
114
+ Notice that the prop_accessor works similar to Ruby's attr_accessor.
115
+ Here are some examples:
116
+
117
+ prop :title, true, String
118
+ prop_reader :title, :body, :author, String
119
+
120
+ To make the definitions look even cleaner, Og provides the property alias:
121
+
122
+ class Category
123
+ property :name, String
124
+ end
125
+
126
+ class Post
127
+ property :title, String
128
+ property :body, String
129
+ property :author, String
130
+ property :create_time, Time
131
+ property :hits, Fixnum
132
+ end
133
+
134
+ class Comment
135
+ property :title, String
136
+ property :body, String
137
+ property :author, String
138
+ property :create_time, Time
139
+ end
140
+
141
+ This is most of the information that Og needs to manage these objects. Before we continue,
142
+ we need to setup the actual RDBMS data store used by Og. Currently, Og has built-in adapters
143
+ for PostgreSQL, MySQL, SQLite3, and Oracle. For this example, we'll use the PostgreSQL adapter,
144
+ so add this code after the class definitions.
145
+
146
+ db = Og::Database.new(
147
+ :database => 'test',
148
+ :adapter => 'psql',
149
+ :user => 'postgres',
150
+ :password => 'navelrulez'
151
+ )
152
+
153
+ Now you are ready to save your first object into Postgres. Add the following code:
154
+
155
+ # create the object
156
+ p = Post.new
157
+ p.title = 'Hello'
158
+ p.body = 'World'
159
+ p.author = 'tml'
160
+
161
+ # save the object in the database
162
+ p.save
163
+
164
+ That's it! Og works behind the scenes doing all the work for you.
165
+ This simple command, p.save, does the following:
166
+
167
+ 1. Creates the database 'test' if it doesn't exist.
168
+ 2. Creates a table to store Post objects if it doesn't exist.
169
+ The table's columns map to the object properties.
170
+ 3. Creates SQL indices.
171
+ 4. Creates any needed sequences.
172
+ 5. Serializes the object into the table.
173
+
174
+ Issue the following SQL to see the result:
175
+
176
+ SELECT * FROM og_post
177
+
178
+ This is nice, but where does the #save method come from?
179
+ Og uses Ruby's advanced introspection features to automatically
180
+ 'enchant' class that define properties. An enchanted class
181
+ provides several methods that will be discussed in the following
182
+ text. These enchanted classes are called *managed* classes.
183
+
184
+ Before going on, let's look at another Og macro that eases object creation:
185
+
186
+ p = Post.create
187
+
188
+ Create automatically calls the save method. Here is another way to save the object:
189
+
190
+ db << p
191
+
192
+ OR
193
+
194
+ db.save(p)
195
+
196
+
197
+ Let's create a Category object.
198
+
199
+ cat = Category.new
200
+ cat.name = 'Programming'
201
+ cat.save
202
+
203
+ If you investigate the generated og_category table, you will
204
+ see an 'oid' column which serves as the primary key. This
205
+ column is added automatically by Og. You can use the oid
206
+ values to lookup objects:
207
+
208
+ cat = Category[1] # loads the category object with oid = 1
209
+
210
+ OR
211
+
212
+ cat = db.load(1, Category)
213
+
214
+ As a convenience, Og allows you to lookup the category
215
+ using the special property 'name':
216
+
217
+ cat = Category['Programming']
218
+
219
+ You can lookup objects by name only if the name property is
220
+ defined.
221
+
222
+ If you want to view Og's SQL, you can enable debug mode by setting this global
223
+ debug (DBG) variable:
224
+
225
+ $DBG = true
226
+
227
+
228
+ === Customizing the Schema and Defining Relations
229
+
230
+ Og makes our blog model persistent through a simple interface. The next step is to
231
+ refine the schema and define relations between the objects:
232
+
233
+ class Post; end
234
+ class Comment; end
235
+
236
+ class Category
237
+ property :name, String
238
+
239
+ many_to_many :posts, Post
240
+
241
+ def initialize(title = nil)
242
+ @title = title
243
+ end
244
+ end
245
+
246
+ class Post
247
+ property :title, String, :sql => 'VARCHAR2(32) NOT NULL'
248
+ property :body, String
249
+ property :author, String
250
+ property :create_time, Time
251
+ property :hits, Fixnum, :sql_index => true
252
+
253
+ has_many :comments, Comment
254
+
255
+ def initialize(title = nil, body = nil, author = nil)
256
+ @title, @body, @author = title, body, author
257
+ @create_time = Time.now
258
+ @hits = 0
259
+ end
260
+ end
261
+
262
+ class Comment
263
+ property :title, String, :sql => 'VARCHAR2(32) NOT NULL'
264
+ property :body, String
265
+ property :author, String
266
+ property :create_time, Time
267
+
268
+ belongs_to :post, Post
269
+
270
+ def initialize(title = nil, body = nil, author = nil)
271
+ @title, @body, @author = title, body, author
272
+ @create_time = Time.now
273
+ end
274
+ end
275
+
276
+ Observe the :sql property option is used to refine
277
+ the generated column type for the title property of Post,
278
+ and how the :sql_index option is used to add an index to
279
+ the generated table.
280
+
281
+ Notice that the initialize methods provide default
282
+ values to all parameters. This is required for all managed objects.
283
+
284
+ Observe the many_to_many, has_many, and belongs_to macros.
285
+ Og uses these macros to define the relations between
286
+ standard Objects. In essence, Og defines a domain specific
287
+ mini language. The following kinds of relations are
288
+ supported:
289
+
290
+ * has_one: has one object of the given type.
291
+
292
+ * has_many: has many objects of the given type.
293
+
294
+ * belongs_to: belongs_to an object of the given type.
295
+
296
+ * many_to_many: defines a many-to-many relation. The corresponding
297
+ rows in the database are linked through a join table.
298
+
299
+ * refers_to: refers to another object.
300
+
301
+ These macros generate the constructs needed to efficiently implement
302
+ the corresponding relations. For example, the belongs_to macro generates
303
+ the property that links to the parent. The many_to_many relation generates
304
+ the join table that links the participating classes.
305
+
306
+ Note that we have to use forward definitions of Post and Comment to satisfy
307
+ Ruby's parser. Workarounds will be provided in a future version.
308
+
309
+ After defining these relations, using and querying the object model is easy:
310
+
311
+ cat = Category.create('Programming')
312
+
313
+ cat.add_post { |p|
314
+ p.title = 'Title'
315
+ p.body = 'Body
316
+ }
317
+
318
+ cat.add_post { |p|
319
+ p.title = 'Another'
320
+ p.body = 'Hello'
321
+ }
322
+
323
+ cat.posts
324
+ => [Post(Title), Post(Another)]
325
+
326
+ cat.posts[0].title
327
+ => Title
328
+
329
+ cat.posts.size
330
+ => 2
331
+
332
+ p = Post[1]
333
+ p.title
334
+ => Title
335
+
336
+ p.categories[0].title
337
+ => 'Programming'
338
+
339
+ c = Comment.new('hello', 'world', 'tml')
340
+ c.post = p
341
+ c.save
342
+
343
+ p.comments.size
344
+ => 1
345
+
346
+ p.add_comment { |c|
347
+ c.title = 'Hi there'
348
+ }
349
+
350
+ p.comments[1].title
351
+ => 'Hi there'
352
+
353
+ com = Comment.new('Hi there')
354
+ p.add_comment(com)
355
+
356
+ All the methods used in the above examples are generated automatically.
357
+ These methods transparently modify the underlying SQL schema using efficient queries.
358
+
359
+ Og provides full access to all features of the underlying RDBMS. Look at the following:
360
+
361
+ post = Post.select("title='Title' and body='Body'")
362
+ post.size
363
+ => 1
364
+ post.hits
365
+ => 0
366
+
367
+ Updating existing objects is easy too:
368
+
369
+ p = Post[1]
370
+ p.title = 'Changed'
371
+ p.save
372
+
373
+ p = Post[1]
374
+ p.title
375
+ => 'Changed'
376
+
377
+ You can also update specific properties, for example:
378
+
379
+ p = Post[1]
380
+ p.update_properties "body='Hello world'"
381
+
382
+ p = Post[1]
383
+ p.body
384
+ => 'Hello world'
385
+
386
+ If you don't like a particular comment, you can easily delete it by doing the following:
387
+
388
+ Comment.delete(comment)
389
+
390
+ OR
391
+
392
+ comment.delete!
393
+
394
+ OR
395
+
396
+ db.delete(comment)
397
+
398
+ To delete all comments for a posting, enter the following:
399
+
400
+ p.delete_all_comments
401
+
402
+ When deleting an object that participates in relations, Og tries
403
+ to delete all objects that belong to this object (ie, cascade deletes).
404
+
405
+ All the generated methods take more parameters to customize their
406
+ behaviour to suit your needs.
407
+
408
+
409
+ === Defining Callbacks
410
+
411
+ Og provides a detailed callback facility allowing you to hook
412
+ into a managed object's Lifecycle. This is a very useful
413
+ feature that can improve your code considerably. To implement
414
+ a callback, you have to define one or more of the following methods
415
+ in your class:
416
+
417
+ * og_pre_insert
418
+ * og_post_insert
419
+ * og_pre_update
420
+ * og_post_update
421
+ * og_pre_insert_update
422
+ * og_post_insert_update
423
+ * self.og_pre_delete
424
+
425
+ For example, the following code defines a callback for the Post class.
426
+
427
+ class Post
428
+ ...
429
+
430
+ def og_post_insert(conn)
431
+ puts 'Hey, a new post was just posted!'
432
+ end
433
+ end
434
+
435
+ When post.save is called, you'll get this alert:
436
+
437
+ p = Post.create('Hello')
438
+ => console: Hey, a new post was just posted!
439
+
440
+
441
+ === Using OOP techniques
442
+
443
+ Og's managed objects are standard Ruby objects, so we can use class inheritance
444
+ and module inclusion to minimize the code we have to write. Here's how we can
445
+ improve the blog schema:
446
+
447
+ class Category
448
+ property :name, String
449
+ many_to_many :posts, Post
450
+
451
+ def initialize(title = nil)
452
+ @title = title
453
+ end
454
+ end
455
+
456
+ class Common
457
+ property :title, String, :sql => 'VARCHAR2(32) NOT NULL'
458
+ property :body, String
459
+ property :author, String
460
+ property :create_time, Time
461
+
462
+ def initialize(title = nil, body = nil, author = nil)
463
+ @title, @body, @author = title, body, author
464
+ @create_time = Time.now
465
+ end
466
+ end
467
+
468
+ class Post < Common
469
+ property :hits, Fixnum, :sql_index => true
470
+ has_many :comments, Comment
471
+
472
+ def initialize(title = nil, body = nil, author = nil)
473
+ super
474
+ @hits = 0
475
+ end
476
+ end
477
+
478
+ class Comment < Common
479
+ belongs_to :post, Post
480
+ end
481
+
482
+ In essence, this feature allows you to create SQL tables using inheritance,
483
+ saving you lots of time when using objects with similar properties. It's also
484
+ less error prone.
485
+
486
+
487
+ === Defining Validation Rules
488
+
489
+ When managing large amounts of data, enforcing data integrity is
490
+ important. Og provides another domain specific mini language that allows
491
+ you to define validation rules in a simple manner. In the following code,
492
+ the blog schema is enriched with hints that allows Og to automatically generate
493
+ validation code:
494
+
495
+ class Common
496
+ property :title, String, :sql => 'NOT NULL VARCHAR(32)'
497
+ property :body, String
498
+ property :author, String
499
+ property :create_time, String
500
+
501
+ validate_value :title
502
+ validate_length :body, :range => 2..100, :msg_long => 'argh'
503
+ validate_format :author, :format => /[a-z]/, :msg => 'wrong format'
504
+
505
+ def initialize(title = nil, body = nil, author = nil)
506
+ @title, @body, @author = title, body, author
507
+ @create_time = Time.now
508
+ end
509
+ end
510
+
511
+ This code demonstrates some validations facilities. Using the validate_value
512
+ macro, we enforce that the 'title' property will have a value. Using the
513
+ validate_length macro, we enforce the minimum and maximum lengths for the
514
+ 'body' property. Using the validate_format macro, we enforce a required
515
+ format for values assigned to the 'author' field.
516
+
517
+ Let's see this validation in practice:
518
+
519
+ c = Comment.new
520
+ c.valid?
521
+ => false
522
+ c.errors.count
523
+ => 3
524
+
525
+ c.title = 'Hello'
526
+ c.valid?
527
+ c.errors.count
528
+ => 2
529
+
530
+ The errors array contains a list of Error objects that point to the offending
531
+ field and contain a descriptive message.
532
+
533
+ With Og, you can customize almost everything! More information can be found in the source code
534
+ (lib/glue/validation.rb). To whet your appetite, here is a list of predefined validation macros:
535
+
536
+ * validate_value
537
+ * validate_format
538
+ * validate_length
539
+ * validate_inclusion
540
+ * validate_confirmation
541
+
542
+
543
+ === TypeMacros
544
+
545
+ If you look at the common class definition, you will notice that the :sql
546
+ option looks kind of ugly:
547
+
548
+ :sql => 'VARCHAR2(32) NOT NULL'
549
+
550
+ When building larger object models, this issue comes up frequently. Og
551
+ provides a elegant solution in the form of type macros:
552
+
553
+ def VarChar(size)
554
+ return String, :sql => "VARCHAR2(#{size}) NOT NULL"
555
+ end
556
+
557
+ property :title, VarChar(30)
558
+
559
+
560
+ === Switching To Another Database
561
+
562
+ While Postgres is a great database, let's assume that the client wants to switch to
563
+ MySQL at the last minute. Don't worry, Og can easily accomodate this by simply changing
564
+ the db reference in the configuration file to look like this and then re-running the example:
565
+
566
+ db = Og::Database.new(
567
+ :database => 'test',
568
+ :adapter => 'mysql',
569
+ :user => 'postgres',
570
+ :password => 'navelrulez'
571
+ )
572
+
573
+ A new MySQL database is automatically created along with all tables, indices, etc. You get all
574
+ this with changing only one line of code!
575
+
576
+
577
+ === Is There More?
578
+
579
+ You betcha! You can to find more about Og by reading the available RDoc documentation and browsing the examples.
580
+
581
+ For any questions regarding Og, feel free to ask on the ruby-talk
582
+ mailing list (which is mirrored to comp.lang.ruby) or contact
583
+ mailto:gm@navel.gr.
584
+
585
+ A Nitro specific mailing list is also available. You can post questions about
586
+ Og to this list. Please subscribe to nitro-general@rubyforge.com. The homepage
587
+ for this list is available here:
588
+
589
+ http://rubyforge.org/mailman/listinfo/nitro-general
590
+
591
+ Note that Og is still under heavy development, so new features are being added frequently.
592
+ Be sure to check back for updates.
593
+
594
+
595
+